From 72f3747d7be670f1e53beb6a118979f80ec74566 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 30 Sep 2019 14:39:46 -0700 Subject: [PATCH] fix(ivy): refresh child components before executing ViewQuery function (#32922) Child component refresh must happen before executing the ViewQueryFn because child components could insert a template from the host that contains the result of the ViewQuery function (see related test added in this PR). PR Close #32922 --- .../core/src/render3/instructions/shared.ts | 13 ++++--- packages/core/test/acceptance/query_spec.ts | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index 8054fde5f5..d26f1691f8 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -430,17 +430,20 @@ export function refreshView( setHostBindings(tView, lView); - const viewQuery = tView.viewQuery; - if (viewQuery !== null) { - executeViewQueryFn(RenderFlags.Update, viewQuery, context); - } - // Refresh child component views. const components = tView.components; if (components !== null) { refreshChildComponents(lView, components); } + // View queries must execute after refreshing child components because a template in this view + // could be inserted in a child component. If the view query executes before child component + // refresh, the template might not yet be inserted. + const viewQuery = tView.viewQuery; + if (viewQuery !== null) { + executeViewQueryFn(RenderFlags.Update, viewQuery, context); + } + // execute view hooks (AfterViewInit, AfterViewChecked) // PERF WARNING: do NOT extract this to a separate function without running benchmarks if (!checkNoChangesMode) { diff --git a/packages/core/test/acceptance/query_spec.ts b/packages/core/test/acceptance/query_spec.ts index f067c42f51..1c02c0483f 100644 --- a/packages/core/test/acceptance/query_spec.ts +++ b/packages/core/test/acceptance/query_spec.ts @@ -265,6 +265,42 @@ describe('query logic', () => { expect(fixture.componentInstance.foo.length).toBe(2); }); + it('should support ViewChild query where template is inserted in child component', () => { + @Component({selector: 'required', template: ''}) + class Required { + } + + @Component({ + selector: 'insertion', + template: `` + }) + class Insertion { + @Input() content !: TemplateRef<{}>; + } + + @Component({ + template: ` + + + + + ` + }) + class App { + @ViewChild(Required, {static: false}) requiredEl !: Required; + viewChildAvailableInAfterViewInit?: boolean; + + ngAfterViewInit() { + this.viewChildAvailableInAfterViewInit = this.requiredEl !== undefined; + } + } + + const fixture = TestBed.configureTestingModule({declarations: [App, Insertion, Required]}) + .createComponent(App); + fixture.detectChanges(); + expect(fixture.componentInstance.viewChildAvailableInAfterViewInit).toBe(true); + }); + }); describe('content queries', () => {