From 28b040db31ddfc6c570db0000f38e840a3ca8068 Mon Sep 17 00:00:00 2001 From: "ovchinnikov.andrey" Date: Thu, 25 Oct 2018 11:06:28 +0300 Subject: [PATCH] DevExtreme widgets should render templates within Angular Zone --- src/core/template.ts | 36 +++++++++++++++++++++-------- tests/src/core/template.spec.ts | 40 ++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/core/template.ts b/src/core/template.ts index 46c2b2ddd..53dded496 100644 --- a/src/core/template.ts +++ b/src/core/template.ts @@ -6,7 +6,9 @@ import { TemplateRef, ViewContainerRef, Input, - Renderer2 + Renderer2, + NgZone, + EmbeddedViewRef } from '@angular/core'; import { DxTemplateHost } from './template-host'; @@ -34,25 +36,41 @@ export class DxTemplateDirective { constructor(private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef, templateHost: DxTemplateHost, - private renderer: Renderer2) { + private renderer: Renderer2, + private zone: NgZone) { templateHost.setTemplate(this); } - render(renderData: RenderData) { - let childView = this.viewContainerRef.createEmbeddedView(this.templateRef, { + private renderTemplate(renderData: RenderData): EmbeddedViewRef { + const childView = this.viewContainerRef.createEmbeddedView(this.templateRef, { '$implicit': renderData.model, index: renderData.index }); - let container = getElement(renderData.container); + + const container = getElement(renderData.container); if (renderData.container) { childView.rootNodes.forEach((element) => { this.renderer.appendChild(container, element); }); } - // =========== WORKAROUND ============= - // https://github.com/angular/angular/issues/12243 - childView['detectChanges'](); - // =========== /WORKAROUND ============= + + return childView; + } + + render(renderData: RenderData) { + let childView; + if (this.zone.isStable) { + childView = this.zone.run(() => { + return this.renderTemplate(renderData); + }); + } else { + childView = this.renderTemplate(renderData); + // =========== WORKAROUND ============= + // https://github.com/angular/angular/issues/12243 + childView['detectChanges'](); + // =========== /WORKAROUND ============= + } + childView.rootNodes.forEach((element) => { if (element.nodeType === 1) { this.renderer.addClass(element, DX_TEMPLATE_WRAPPER_CLASS); diff --git a/tests/src/core/template.spec.ts b/tests/src/core/template.spec.ts index 78b0db7ce..d49e87d52 100644 --- a/tests/src/core/template.spec.ts +++ b/tests/src/core/template.spec.ts @@ -93,13 +93,21 @@ export class DxTestComponent extends DxComponent implements AfterViewInit { return new DxTestWidget(element, options); } - ngAfterViewInit() { + renderTemplate(model) { + const element = this.element.nativeElement; + element.textContent = ''; this.templates[0].render({ - model: {}, - container: this.element.nativeElement, + model: model, + container: element, index: 5 }); } + + ngAfterViewInit() { + this.renderTemplate({ + value: () => '' + }); + } } @Component({ @@ -109,6 +117,7 @@ export class DxTestComponent extends DxComponent implements AfterViewInit { }) export class TestContainerComponent { @ViewChild(DxTestWidgetComponent) widget: DxTestWidgetComponent; + @ViewChild(DxTestComponent) testComponent: DxTestComponent; @Output() onInnerElementClicked = new EventEmitter(); @@ -193,5 +202,30 @@ describe('DevExtreme Angular widget\'s template', () => { expect(element.textContent).toBe('index: 5'); }); + fit('should be created within Angular Zone', () => { + TestBed.overrideComponent(TestContainerComponent, { + set: { + template: ` + +
+
+
+
+ `} + }); + + let fixture = TestBed.createComponent(TestContainerComponent); + fixture.detectChanges(); + + fixture.ngZone.runOutsideAngular(() => { + fixture.componentInstance.testComponent.renderTemplate({ + value: () => { + expect(fixture.ngZone.isStable).toBe(false); + } + }); + }); + + fixture.nativeElement.querySelector('.elem').click(); + }); });