angular-cn/packages/core/test/acceptance/query_spec.ts

567 lines
22 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
describe('query logic', () => {
beforeEach(() => {
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
TestBed.configureTestingModule({
declarations: [
AppComp, QueryComp, SimpleCompA, SimpleCompB, StaticViewQueryComp, TextDirective,
SubclassStaticViewQueryComp, StaticContentQueryComp, SubclassStaticContentQueryComp,
QueryCompWithChanges, StaticContentQueryDir
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
]
});
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
describe('view queries', () => {
it('should return Component instances when Components are labeled and retrieved', () => {
const template = `
<div><simple-comp-a #viewQuery></simple-comp-a></div>
<div><simple-comp-b #viewQuery></simple-comp-b></div>
`;
const fixture = initWithTemplate(QueryComp, template);
const comp = fixture.componentInstance;
expect(comp.viewChild).toBeAnInstanceOf(SimpleCompA);
expect(comp.viewChildren.first).toBeAnInstanceOf(SimpleCompA);
expect(comp.viewChildren.last).toBeAnInstanceOf(SimpleCompB);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
it('should return ElementRef when HTML element is labeled and retrieved', () => {
const template = `
<div #viewQuery></div>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(QueryComp, template);
const comp = fixture.componentInstance;
expect(comp.viewChild).toBeAnInstanceOf(ElementRef);
expect(comp.viewChildren.first).toBeAnInstanceOf(ElementRef);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
onlyInIvy('multiple local refs are supported in Ivy')
.it('should return ElementRefs when HTML elements are labeled and retrieved', () => {
const template = `
<div #viewQuery #first>A</div>
<div #viewQuery #second>B</div>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(QueryComp, template);
const comp = fixture.componentInstance;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.viewChild).toBeAnInstanceOf(ElementRef);
expect(comp.viewChild.nativeElement).toBe(fixture.debugElement.children[0].nativeElement);
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.viewChildren.first).toBeAnInstanceOf(ElementRef);
expect(comp.viewChildren.last).toBeAnInstanceOf(ElementRef);
expect(comp.viewChildren.length).toBe(2);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
it('should return TemplateRef when template is labeled and retrieved', () => {
const template = `
<ng-template #viewQuery></ng-template>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(QueryComp, template);
const comp = fixture.componentInstance;
expect(comp.viewChildren.first).toBeAnInstanceOf(TemplateRef);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
onlyInIvy('multiple local refs are supported in Ivy')
.it('should return TemplateRefs when templates are labeled and retrieved', () => {
const template = `
<ng-template #viewQuery></ng-template>
<ng-template #viewQuery></ng-template>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(QueryComp, template);
const comp = fixture.componentInstance;
expect(comp.viewChild).toBeAnInstanceOf(TemplateRef);
expect(comp.viewChild.elementRef.nativeElement)
.toBe(fixture.debugElement.childNodes[0].nativeNode);
expect(comp.viewChildren.first).toBeAnInstanceOf(TemplateRef);
expect(comp.viewChildren.last).toBeAnInstanceOf(TemplateRef);
expect(comp.viewChildren.length).toBe(2);
});
it('should set static view child queries in creation mode (and just in creation mode)', () => {
const fixture = TestBed.createComponent(StaticViewQueryComp);
const component = fixture.componentInstance;
// static ViewChild query should be set in creation mode, before CD runs
expect(component.textDir).toBeAnInstanceOf(TextDirective);
expect(component.textDir.text).toEqual('');
expect(component.setEvents).toEqual(['textDir set']);
// dynamic ViewChild query should not have been resolved yet
expect(component.foo).not.toBeDefined();
const span = fixture.nativeElement.querySelector('span');
fixture.detectChanges();
expect(component.textDir.text).toEqual('some text');
expect(component.foo.nativeElement).toBe(span);
expect(component.setEvents).toEqual(['textDir set', 'foo set']);
});
it('should support static view child queries inherited from superclasses', () => {
const fixture = TestBed.createComponent(SubclassStaticViewQueryComp);
const component = fixture.componentInstance;
const divs = fixture.nativeElement.querySelectorAll('div');
const spans = fixture.nativeElement.querySelectorAll('span');
// static ViewChild queries should be set in creation mode, before CD runs
expect(component.textDir).toBeAnInstanceOf(TextDirective);
expect(component.textDir.text).toEqual('');
expect(component.bar.nativeElement).toEqual(divs[1]);
// dynamic ViewChild queries should not have been resolved yet
expect(component.foo).not.toBeDefined();
expect(component.baz).not.toBeDefined();
fixture.detectChanges();
expect(component.textDir.text).toEqual('some text');
expect(component.foo.nativeElement).toBe(spans[0]);
expect(component.baz.nativeElement).toBe(spans[1]);
});
it('should support multiple static view queries (multiple template passes)', () => {
const template = `
<static-view-query-comp></static-view-query-comp>
<static-view-query-comp></static-view-query-comp>
`;
TestBed.overrideComponent(AppComp, {set: new Component({template})});
const fixture = TestBed.createComponent(AppComp);
const firstComponent = fixture.debugElement.children[0].injector.get(StaticViewQueryComp);
const secondComponent = fixture.debugElement.children[1].injector.get(StaticViewQueryComp);
// static ViewChild query should be set in creation mode, before CD runs
expect(firstComponent.textDir).toBeAnInstanceOf(TextDirective);
expect(secondComponent.textDir).toBeAnInstanceOf(TextDirective);
expect(firstComponent.textDir.text).toEqual('');
expect(secondComponent.textDir.text).toEqual('');
expect(firstComponent.setEvents).toEqual(['textDir set']);
expect(secondComponent.setEvents).toEqual(['textDir set']);
// dynamic ViewChild query should not have been resolved yet
expect(firstComponent.foo).not.toBeDefined();
expect(secondComponent.foo).not.toBeDefined();
const spans = fixture.nativeElement.querySelectorAll('span');
fixture.detectChanges();
expect(firstComponent.textDir.text).toEqual('some text');
expect(secondComponent.textDir.text).toEqual('some text');
expect(firstComponent.foo.nativeElement).toBe(spans[0]);
expect(secondComponent.foo.nativeElement).toBe(spans[1]);
expect(firstComponent.setEvents).toEqual(['textDir set', 'foo set']);
expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
});
describe('content queries', () => {
it('should return Component instance when Component is labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<simple-comp-a #contentQuery></simple-comp-a>
</local-ref-query-component>
`;
const fixture = initWithTemplate(AppComp, template);
const comp = fixture.debugElement.children[0].references['q'];
expect(comp.contentChild).toBeAnInstanceOf(SimpleCompA);
expect(comp.contentChildren.first).toBeAnInstanceOf(SimpleCompA);
});
onlyInIvy('multiple local refs are supported in Ivy')
.it('should return Component instances when Components are labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<simple-comp-a #contentQuery></simple-comp-a>
<simple-comp-b #contentQuery></simple-comp-b>
</local-ref-query-component>
`;
const fixture = initWithTemplate(AppComp, template);
const comp = fixture.debugElement.children[0].references['q'];
expect(comp.contentChild).toBeAnInstanceOf(SimpleCompA);
expect(comp.contentChildren.first).toBeAnInstanceOf(SimpleCompA);
expect(comp.contentChildren.last).toBeAnInstanceOf(SimpleCompB);
expect(comp.contentChildren.length).toBe(2);
});
it('should return ElementRef when HTML element is labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<div #contentQuery></div>
</local-ref-query-component>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(AppComp, template);
const comp = fixture.debugElement.children[0].references['q'];
expect(comp.contentChildren.first).toBeAnInstanceOf(ElementRef);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
onlyInIvy('multiple local refs are supported in Ivy')
.it('should return ElementRefs when HTML elements are labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<div #contentQuery></div>
<div #contentQuery></div>
</local-ref-query-component>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(AppComp, template);
const firstChild = fixture.debugElement.children[0];
const comp = firstChild.references['q'];
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.contentChild).toBeAnInstanceOf(ElementRef);
expect(comp.contentChild.nativeElement).toBe(firstChild.children[0].nativeElement);
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.contentChildren.first).toBeAnInstanceOf(ElementRef);
expect(comp.contentChildren.last).toBeAnInstanceOf(ElementRef);
expect(comp.contentChildren.length).toBe(2);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
it('should return TemplateRef when template is labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<ng-template #contentQuery></ng-template>
</local-ref-query-component>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(AppComp, template);
const comp = fixture.debugElement.children[0].references['q'];
expect(comp.contentChildren.first).toBeAnInstanceOf(TemplateRef);
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
onlyInIvy('multiple local refs are supported in Ivy')
.it('should return TemplateRefs when templates are labeled and retrieved', () => {
const template = `
<local-ref-query-component #q>
<ng-template #contentQuery></ng-template>
<ng-template #contentQuery></ng-template>
</local-ref-query-component>
`;
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
const fixture = initWithTemplate(AppComp, template);
const firstChild = fixture.debugElement.children[0];
const comp = firstChild.references['q'];
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.contentChild).toBeAnInstanceOf(TemplateRef);
expect(comp.contentChild.elementRef.nativeElement)
.toBe(firstChild.childNodes[0].nativeNode);
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
expect(comp.contentChildren.first).toBeAnInstanceOf(TemplateRef);
expect(comp.contentChildren.last).toBeAnInstanceOf(TemplateRef);
expect(comp.contentChildren.length).toBe(2);
});
it('should set static content child queries in creation mode (and just in creation mode)',
() => {
const template = `
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
<static-content-query-comp>
<div [text]="text"></div>
<span #foo></span>
</static-content-query-comp>
`;
TestBed.overrideComponent(AppComp, {set: new Component({template})});
const fixture = TestBed.createComponent(AppComp);
const component = fixture.debugElement.children[0].injector.get(StaticContentQueryComp);
// static ContentChild query should be set in creation mode, before CD runs
expect(component.textDir).toBeAnInstanceOf(TextDirective);
expect(component.textDir.text).toEqual('');
expect(component.setEvents).toEqual(['textDir set']);
// dynamic ContentChild query should not have been resolved yet
expect(component.foo).not.toBeDefined();
const span = fixture.nativeElement.querySelector('span');
(fixture.componentInstance as any).text = 'some text';
fixture.detectChanges();
expect(component.textDir.text).toEqual('some text');
expect(component.foo.nativeElement).toBe(span);
expect(component.setEvents).toEqual(['textDir set', 'foo set']);
});
it('should support static content child queries inherited from superclasses', () => {
const template = `
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
<subclass-static-content-query-comp>
<div [text]="text"></div>
<span #foo></span>
<div #bar></div>
<span #baz></span>
</subclass-static-content-query-comp>
`;
TestBed.overrideComponent(AppComp, {set: new Component({template})});
const fixture = TestBed.createComponent(AppComp);
const component =
fixture.debugElement.children[0].injector.get(SubclassStaticContentQueryComp);
const divs = fixture.nativeElement.querySelectorAll('div');
const spans = fixture.nativeElement.querySelectorAll('span');
// static ContentChild queries should be set in creation mode, before CD runs
expect(component.textDir).toBeAnInstanceOf(TextDirective);
expect(component.textDir.text).toEqual('');
expect(component.bar.nativeElement).toEqual(divs[1]);
// dynamic ContentChild queries should not have been resolved yet
expect(component.foo).not.toBeDefined();
expect(component.baz).not.toBeDefined();
(fixture.componentInstance as any).text = 'some text';
fixture.detectChanges();
expect(component.textDir.text).toEqual('some text');
expect(component.foo.nativeElement).toBe(spans[0]);
expect(component.baz.nativeElement).toBe(spans[1]);
});
it('should set static content child queries on directives', () => {
const template = `
<div staticContentQueryDir>
<div [text]="text"></div>
<span #foo></span>
</div>
`;
TestBed.overrideComponent(AppComp, {set: new Component({template})});
const fixture = TestBed.createComponent(AppComp);
const component = fixture.debugElement.children[0].injector.get(StaticContentQueryDir);
// static ContentChild query should be set in creation mode, before CD runs
expect(component.textDir).toBeAnInstanceOf(TextDirective);
expect(component.textDir.text).toEqual('');
expect(component.setEvents).toEqual(['textDir set']);
// dynamic ContentChild query should not have been resolved yet
expect(component.foo).not.toBeDefined();
const span = fixture.nativeElement.querySelector('span');
(fixture.componentInstance as any).text = 'some text';
fixture.detectChanges();
expect(component.textDir.text).toEqual('some text');
expect(component.foo.nativeElement).toBe(span);
expect(component.setEvents).toEqual(['textDir set', 'foo set']);
});
it('should support multiple content query components (multiple template passes)', () => {
const template = `
<static-content-query-comp>
<div [text]="text"></div>
<span #foo></span>
</static-content-query-comp>
<static-content-query-comp>
<div [text]="text"></div>
<span #foo></span>
</static-content-query-comp>
`;
TestBed.overrideComponent(AppComp, {set: new Component({template})});
const fixture = TestBed.createComponent(AppComp);
const firstComponent = fixture.debugElement.children[0].injector.get(StaticContentQueryComp);
const secondComponent = fixture.debugElement.children[1].injector.get(StaticContentQueryComp);
// static ContentChild query should be set in creation mode, before CD runs
expect(firstComponent.textDir).toBeAnInstanceOf(TextDirective);
expect(secondComponent.textDir).toBeAnInstanceOf(TextDirective);
expect(firstComponent.textDir.text).toEqual('');
expect(secondComponent.textDir.text).toEqual('');
expect(firstComponent.setEvents).toEqual(['textDir set']);
expect(secondComponent.setEvents).toEqual(['textDir set']);
// dynamic ContentChild query should not have been resolved yet
expect(firstComponent.foo).not.toBeDefined();
expect(secondComponent.foo).not.toBeDefined();
const spans = fixture.nativeElement.querySelectorAll('span');
(fixture.componentInstance as any).text = 'some text';
fixture.detectChanges();
expect(firstComponent.textDir.text).toEqual('some text');
expect(secondComponent.textDir.text).toEqual('some text');
expect(firstComponent.foo.nativeElement).toBe(spans[0]);
expect(secondComponent.foo.nativeElement).toBe(spans[1]);
expect(firstComponent.setEvents).toEqual(['textDir set', 'foo set']);
expect(secondComponent.setEvents).toEqual(['textDir set', 'foo set']);
});
});
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
describe('observable interface', () => {
it('should allow observing changes to query list', () => {
const fixture = TestBed.createComponent(QueryCompWithChanges);
let changes = 0;
fixture.detectChanges();
fixture.componentInstance.foos.changes.subscribe((value: any) => {
changes += 1;
expect(value).toBe(fixture.componentInstance.foos);
});
// refresh without setting dirty - no emit
fixture.detectChanges();
expect(changes).toBe(0);
// refresh with setting dirty - emit
fixture.componentInstance.showing = true;
fixture.detectChanges();
expect(changes).toBe(1);
});
});
});
function initWithTemplate(compType: Type<any>, template: string) {
TestBed.overrideComponent(compType, {set: new Component({template})});
const fixture = TestBed.createComponent(compType);
fixture.detectChanges();
return fixture;
}
@Component({selector: 'local-ref-query-component', template: '<ng-content></ng-content>'})
class QueryComp {
@ViewChild('viewQuery') viewChild !: any;
@ContentChild('contentQuery') contentChild !: any;
@ViewChildren('viewQuery') viewChildren !: QueryList<any>;
@ContentChildren('contentQuery') contentChildren !: QueryList<any>;
}
@Component({selector: 'app-comp', template: ``})
class AppComp {
}
@Component({selector: 'simple-comp-a', template: ''})
class SimpleCompA {
}
@Component({selector: 'simple-comp-b', template: ''})
class SimpleCompB {
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
}
@Directive({selector: '[text]'})
class TextDirective {
@Input() text = '';
}
@Component({
selector: 'static-view-query-comp',
template: `
<div [text]="text"></div>
<span #foo></span>
`
})
class StaticViewQueryComp {
private _textDir !: TextDirective;
private _foo !: ElementRef;
setEvents: string[] = [];
@ViewChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
this._textDir = value;
}
@ViewChild('foo', {static: false})
get foo(): ElementRef { return this._foo; }
set foo(value: ElementRef) {
this.setEvents.push('foo set');
this._foo = value;
}
text = 'some text';
}
@Component({
selector: 'subclass-static-view-query-comp',
template: `
<div [text]="text"></div>
<span #foo></span>
<div #bar></div>
<span #baz></span>
`
})
class SubclassStaticViewQueryComp extends StaticViewQueryComp {
@ViewChild('bar', {static: true})
bar !: ElementRef;
@ViewChild('baz', {static: false})
baz !: ElementRef;
}
@Component({selector: 'static-content-query-comp', template: `<ng-content></ng-content>`})
class StaticContentQueryComp {
private _textDir !: TextDirective;
private _foo !: ElementRef;
setEvents: string[] = [];
@ContentChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
this._textDir = value;
}
@ContentChild('foo', {static: false})
get foo(): ElementRef { return this._foo; }
set foo(value: ElementRef) {
this.setEvents.push('foo set');
this._foo = value;
}
}
@Directive({selector: '[staticContentQueryDir]'})
class StaticContentQueryDir {
private _textDir !: TextDirective;
private _foo !: ElementRef;
setEvents: string[] = [];
@ContentChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
this._textDir = value;
}
@ContentChild('foo', {static: false})
get foo(): ElementRef { return this._foo; }
set foo(value: ElementRef) {
this.setEvents.push('foo set');
this._foo = value;
}
}
feat(core): allow users to define timing of ViewChild/ContentChild queries (#28810) Prior to this commit, the timing of `ViewChild`/`ContentChild` query resolution depended on the results of each query. If any results for a particular query were nested inside embedded views (e.g. *ngIfs), that query would be resolved after change detection ran. Otherwise, the query would be resolved as soon as nodes were created. This inconsistency in resolution timing had the potential to cause confusion because query results would sometimes be available in ngOnInit, but sometimes wouldn't be available until ngAfterContentInit or ngAfterViewInit. Code depending on a query result could suddenly stop working as soon as an *ngIf or an *ngFor was added to the template. With this commit, users can dictate when they want a particular `ViewChild` or `ContentChild` query to be resolved with the `static` flag. For example, one can mark a particular query as `static: false` to ensure change detection always runs before its results are set: ```ts @ContentChild('foo', {static: false}) foo !: ElementRef; ``` This means that even if there isn't a query result wrapped in an *ngIf or an *ngFor now, adding one to the template later won't change the timing of the query resolution and potentially break your component. Similarly, if you know that your query needs to be resolved earlier (e.g. you need results in an ngOnInit hook), you can mark it as `static: true`. ```ts @ViewChild(TemplateRef, {static: true}) foo !: TemplateRef; ``` Note: this means that your component will not support *ngIf results. If you do not supply a `static` option when creating your `ViewChild` or `ContentChild` query, the default query resolution timing will kick in. Note: This new option only applies to `ViewChild` and `ContentChild` queries, not `ViewChildren` or `ContentChildren` queries, as those types already resolve after CD runs. PR Close #28810
2019-02-18 17:38:14 -05:00
@Component({selector: 'subclass-static-content-query-comp', template: `<ng-content></ng-content>`})
class SubclassStaticContentQueryComp extends StaticContentQueryComp {
@ContentChild('bar', {static: true})
bar !: ElementRef;
@ContentChild('baz', {static: false})
baz !: ElementRef;
}
@Component({
selector: 'query-with-changes',
template: `
<div *ngIf="showing" #foo></div>
`
})
export class QueryCompWithChanges {
@ViewChildren('foo') foos !: QueryList<any>;
showing = false;
}