fix(core): allow null / undefined values in query results (#35796)

Before this change ngIvy implementation of queries would throw upon
encountering null / undefined query result collected from an embedded
view. It turns out that we might have a provider that explicitly provides
a null / undefined value in a place of a token queried for.

This commit removes a check from the ngIvy query implementation that was
asserting on a query result to be defined.

Fixes #35673

PR Close #35796
This commit is contained in:
Pawel Kozlowski 2020-03-02 15:55:58 +01:00 committed by atscott
parent 44e47da4cf
commit 5652fb1018
2 changed files with 73 additions and 3 deletions

View File

@ -363,9 +363,7 @@ function collectQueryResults<T>(tView: TView, lView: LView, queryIndex: number,
for (let i = 0; i < tQueryMatches.length; i += 2) { for (let i = 0; i < tQueryMatches.length; i += 2) {
const tNodeIdx = tQueryMatches[i]; const tNodeIdx = tQueryMatches[i];
if (tNodeIdx > 0) { if (tNodeIdx > 0) {
const viewResult = lViewResults[i / 2]; result.push(lViewResults[i / 2] as T);
ngDevMode && assertDefined(viewResult, 'materialized query result should be defined');
result.push(viewResult as T);
} else { } else {
const childQueryIndex = tQueryMatches[i + 1]; const childQueryIndex = tQueryMatches[i + 1];

View File

@ -1425,6 +1425,78 @@ describe('query logic', () => {
expect(fixture.componentInstance.queryResults.last).toBeAnInstanceOf(WithMultiProvider); expect(fixture.componentInstance.queryResults.last).toBeAnInstanceOf(WithMultiProvider);
}); });
it('should allow undefined provider value in a [View/Content]Child queries', () => {
@Directive({selector: '[group]'})
class GroupDir {
}
@Directive(
{selector: '[undefinedGroup]', providers: [{provide: GroupDir, useValue: undefined}]})
class UndefinedGroup {
}
@Component({
template: `
<div group></div>
<ng-template [ngIf]="true">
<div undefinedGroup></div>
</ng-template>
`
})
class App {
@ViewChild(GroupDir) group !: GroupDir;
}
TestBed.configureTestingModule(
{declarations: [App, GroupDir, UndefinedGroup], imports: [CommonModule]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(fixture.componentInstance.group).toBeAnInstanceOf(GroupDir);
});
it('should allow null / undefined provider value in a [View/Content]Children queries', () => {
@Directive({selector: '[group]'})
class GroupDir {
}
@Directive({selector: '[nullGroup]', providers: [{provide: GroupDir, useValue: null}]})
class NullGroup {
}
@Directive(
{selector: '[undefinedGroup]', providers: [{provide: GroupDir, useValue: undefined}]})
class UndefinedGroup {
}
@Component({
template: `
<ng-template [ngIf]="true">
<div nullGroup></div>
</ng-template>
<div group></div>
<ng-template [ngIf]="true">
<div undefinedGroup></div>
</ng-template>
`
})
class App {
@ViewChildren(GroupDir) groups !: QueryList<GroupDir>;
}
TestBed.configureTestingModule(
{declarations: [App, GroupDir, NullGroup, UndefinedGroup], imports: [CommonModule]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const queryList = fixture.componentInstance.groups;
expect(queryList.length).toBe(3);
const groups = queryList.toArray();
expect(groups[0]).toBeNull();
expect(groups[1]).toBeAnInstanceOf(GroupDir);
expect(groups[2]).toBeUndefined();
});
}); });