fix(ivy): Content Queries inheritance fix (#28324)
Prior to this change contentQueriesRefresh functions that represent refresh logic for @ContentQuery list were not composable, which caused problems in case one Directive inherits another one and both of them contain Content Queries. Due to the fact that we used indices to reference queries in refresh function, results were placed into wrong Queries. In order to avoid that we no longer use indices to reference queries and instead maintain current content query index while iterating through them. This allows us to compose contentQueriesRefresh functions and make inheritance feature work with Content Queries. PR Close #28324
This commit is contained in:

committed by
Jason Aden

parent
ebac5dba38
commit
bb94434d85
@ -1554,14 +1554,14 @@ describe('compiler compliance', () => {
|
||||
return new (t || ContentQueryComponent)();
|
||||
},
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false), dirIndex);
|
||||
$r3$.ɵcontentQuery(dirIndex, SomeDirective, true);
|
||||
$r3$.ɵcontentQuery(dirIndex, SomeDirective, false);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && ($instance$.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && ($instance$.someDirList = $tmp$));
|
||||
},
|
||||
ngContentSelectors: _c0,
|
||||
consts: 2,
|
||||
@ -1613,14 +1613,14 @@ describe('compiler compliance', () => {
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false), dirIndex);
|
||||
$r3$.ɵcontentQuery(dirIndex, $e0_attrs$, true);
|
||||
$r3$.ɵcontentQuery(dirIndex, $e1_attrs$, false);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRefs = $tmp$));
|
||||
},
|
||||
…
|
||||
});`;
|
||||
@ -1666,18 +1666,18 @@ describe('compiler compliance', () => {
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$ , true, TemplateRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false, TemplateRef), dirIndex);
|
||||
$r3$.ɵcontentQuery(dirIndex, $e0_attrs$ , true, TemplateRef);
|
||||
$r3$.ɵcontentQuery(dirIndex, SomeDirective, true, ElementRef);
|
||||
$r3$.ɵcontentQuery(dirIndex, $e1_attrs$, false, ElementRef);
|
||||
$r3$.ɵcontentQuery(dirIndex, SomeDirective, false, TemplateRef);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && (instance.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 2)))) && (instance.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 3)))) && (instance.someDirs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadContentQuery())) && (instance.someDirs = $tmp$));
|
||||
},
|
||||
…
|
||||
});`;
|
||||
|
@ -12,6 +12,18 @@ import {NgtscTestEnvironment} from './env';
|
||||
|
||||
const trim = (input: string): string => input.replace(/\s+/g, ' ').trim();
|
||||
|
||||
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
||||
|
||||
const viewQueryRegExp = (descend: boolean, ref?: string): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵviewQuery\\(\\w+, ${descend}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
const contentQueryRegExp = (predicate: string, descend: boolean, ref?: string): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵcontentQuery\\(dirIndex, ${predicate}, ${descend}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
describe('ngtsc behavioral tests', () => {
|
||||
if (!NgtscTestEnvironment.supported) {
|
||||
// These tests should be excluded from the non-Bazel build.
|
||||
@ -706,14 +718,6 @@ describe('ngtsc behavioral tests', () => {
|
||||
});
|
||||
|
||||
it('should generate queries for components', () => {
|
||||
|
||||
// Helper functions to construct RegExps for output validation
|
||||
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
||||
const queryRegExp = (fnName: string, descend: boolean, ref?: string | null): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵ${fnName}\\(\\w+, ${descend}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
env.tsconfig();
|
||||
env.write(`test.ts`, `
|
||||
import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core';
|
||||
@ -740,23 +744,10 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(jsContents).toMatch(varRegExp('test1'));
|
||||
expect(jsContents).toMatch(varRegExp('test2'));
|
||||
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, false)`);
|
||||
expect(jsContents)
|
||||
.toMatch(
|
||||
// match `i0.ɵquery(_c0, true, TemplateRef)`
|
||||
queryRegExp('query', true, 'TemplateRef'));
|
||||
expect(jsContents)
|
||||
.toMatch(
|
||||
// match `i0.ɵquery(_c0, true)`
|
||||
queryRegExp('query', true));
|
||||
expect(jsContents)
|
||||
.toMatch(
|
||||
// match `i0.ɵviewQuery(_c0, true)`
|
||||
queryRegExp('viewQuery', true));
|
||||
expect(jsContents)
|
||||
.toMatch(
|
||||
// match `i0.ɵviewQuery(_c0, true)`
|
||||
queryRegExp('viewQuery', true));
|
||||
// match `i0.ɵcontentQuery(dirIndex, _c1, true, TemplateRef)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef'));
|
||||
// match `i0.ɵviewQuery(_c2, true)`
|
||||
expect(jsContents).toMatch(viewQueryRegExp(true));
|
||||
});
|
||||
|
||||
it('should handle queries that use forwardRef', () => {
|
||||
@ -777,8 +768,10 @@ describe('ngtsc behavioral tests', () => {
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, true)`);
|
||||
expect(jsContents).toContain(`i0.ɵquery(ViewContainerRef, true)`);
|
||||
// match `i0.ɵcontentQuery(dirIndex, TemplateRef, true)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true));
|
||||
// match `i0.ɵcontentQuery(dirIndex, ViewContainerRef, true)`
|
||||
expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true));
|
||||
});
|
||||
|
||||
it('should generate host listeners for components', () => {
|
||||
|
Reference in New Issue
Block a user