diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index ba853e58f1..5fbe2c74de 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -421,6 +421,7 @@ function renderComponentOrTemplate( // Element was stored at 0 in data and directive was stored at 0 in directives // in renderComponent() setHostBindings(getTView(), hostView); + refreshDynamicEmbeddedViews(hostView); componentRefresh(HEADER_OFFSET, false); } } finally { @@ -1828,7 +1829,7 @@ export function containerRefreshEnd(): void { * Goes over dynamic embedded views (ones created through ViewContainerRef APIs) and refreshes them * by executing an associated template function. */ -function refreshDynamicEmbeddedViews(lViewData: LViewData) { +export function refreshDynamicEmbeddedViews(lViewData: LViewData) { for (let current = getLViewChild(lViewData); current !== null; current = current[NEXT]) { // Note: current can be an LViewData or an LContainer instance, but here we are only interested // in LContainer. We can tell it's an LContainer because its length is less than the LViewData diff --git a/packages/core/test/render3/view_container_ref_spec.ts b/packages/core/test/render3/view_container_ref_spec.ts index 7df73d2830..e1069f398f 100644 --- a/packages/core/test/render3/view_container_ref_spec.ts +++ b/packages/core/test/render3/view_container_ref_spec.ts @@ -1771,6 +1771,28 @@ describe('ViewContainerRef', () => { describe('view engine compatibility', () => { + @Component({selector: 'app', template: ''}) + class AppCmpt { + static ngComponentDef = defineComponent({ + type: AppCmpt, + selectors: [['app']], + factory: () => new AppCmpt( + directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()), + consts: 0, + vars: 0, + template: (rf: RenderFlags, cmp: AppCmpt) => {} + }); + + constructor(private _vcRef: ViewContainerRef, private _cfResolver: ComponentFactoryResolver) { + } + + insert(comp: any) { + this._vcRef.createComponent(this._cfResolver.resolveComponentFactory(comp)); + } + + clear() { this._vcRef.clear(); } + } + // https://stackblitz.com/edit/angular-xxpffd?file=src%2Findex.html it('should allow injecting VCRef into the root (bootstrapped) component', () => { @@ -1781,32 +1803,11 @@ describe('ViewContainerRef', () => { } }, 1, 0); - @Component({selector: 'app', template: ''}) - class AppCmpt { - static ngComponentDef = defineComponent({ - type: AppCmpt, - selectors: [['app']], - factory: () => new AppCmpt( - directiveInject(ViewContainerRef as any), injectComponentFactoryResolver()), - consts: 0, - vars: 0, - template: (rf: RenderFlags, cmp: AppCmpt) => {} - }); - - constructor( - private _vcRef: ViewContainerRef, private _cfResolver: ComponentFactoryResolver) {} - - insert() { - this._vcRef.createComponent(this._cfResolver.resolveComponentFactory(DynamicComponent)); - } - - clear() { this._vcRef.clear(); } - } const fixture = new ComponentFixture(AppCmpt); expect(fixture.outerHtml).toBe('
'); - fixture.component.insert(); + fixture.component.insert(DynamicComponent); fixture.update(); expect(fixture.outerHtml) .toBe('
inserted dynamically'); @@ -1815,5 +1816,45 @@ describe('ViewContainerRef', () => { fixture.update(); expect(fixture.outerHtml).toBe('
'); }); + + it('should check bindings for components dynamically created by root component', () => { + class DynamicCompWithBindings { + checkCount = 0; + + ngDoCheck() { this.checkCount++; } + + /** check count: {{ checkCount }} */ + static ngComponentDef = defineComponent({ + type: DynamicCompWithBindings, + selectors: [['dynamic-cmpt-with-bindings']], + factory: () => new DynamicCompWithBindings(), + consts: 1, + vars: 1, + template: (rf: RenderFlags, ctx: DynamicCompWithBindings) => { + if (rf & RenderFlags.Create) { + text(0); + } + if (rf & RenderFlags.Update) { + textBinding(0, interpolation1('check count: ', ctx.checkCount, '')); + } + } + }); + } + + const fixture = new ComponentFixture(AppCmpt); + expect(fixture.outerHtml).toBe('
'); + + fixture.component.insert(DynamicCompWithBindings); + fixture.update(); + expect(fixture.outerHtml) + .toBe( + '
check count: 1'); + + fixture.update(); + expect(fixture.outerHtml) + .toBe( + '
check count: 2'); + }); + }); });