fix(ivy): View Queries inheritance fix (#28309)
Prior to this change `viewQuery` functions that represent @ViewQuery list were not composable, which caused problems in case one Component/Directive inherits another one and both of them contain View Queries. Due to the fact that we used indices to reference queries, resulting query set was corrupted (child component queries were overridden by super class ones). In order to avoid that we no longer use indices assigned at compile time and instead maintain current view query index while iterating through them. This allows us to compose `viewQuery` functions and make inheritance feature work with View Queries. PR Close #28309
This commit is contained in:

committed by
Jason Aden

parent
9f9024b7a1
commit
9098225ff0
@ -1376,20 +1376,20 @@ describe('compiler compliance', () => {
|
||||
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵquery(0, SomeDirective, true);
|
||||
$r3$.ɵquery(1, SomeDirective, true);
|
||||
$r3$.ɵviewQuery(SomeDirective, true);
|
||||
$r3$.ɵviewQuery(SomeDirective, true);
|
||||
}
|
||||
if (rf & 2) {
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.someDirs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDirs = $tmp$));
|
||||
}
|
||||
},
|
||||
consts: 3,
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function ViewQueryComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(2, "div", $e0_attrs$);
|
||||
$r3$.ɵelement(0, "div", $e0_attrs$);
|
||||
}
|
||||
},
|
||||
directives: function () { return [SomeDirective]; },
|
||||
@ -1434,13 +1434,13 @@ describe('compiler compliance', () => {
|
||||
…
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵquery(0, $e0_attrs$, true);
|
||||
$r3$.ɵquery(1, $e1_attrs$, true);
|
||||
$r3$.ɵviewQuery($e0_attrs$, true);
|
||||
$r3$.ɵviewQuery($e1_attrs$, true);
|
||||
}
|
||||
if (rf & 2) {
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRefs = $tmp$));
|
||||
}
|
||||
},
|
||||
…
|
||||
@ -1489,17 +1489,17 @@ describe('compiler compliance', () => {
|
||||
…
|
||||
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵquery(0, $e0_attrs$, true, TemplateRef);
|
||||
$r3$.ɵquery(1, SomeDirective, true, ElementRef);
|
||||
$r3$.ɵquery(2, $e1_attrs$, true, ElementRef);
|
||||
$r3$.ɵquery(3, SomeDirective, true, TemplateRef);
|
||||
$r3$.ɵviewQuery($e0_attrs$, true, TemplateRef);
|
||||
$r3$.ɵviewQuery(SomeDirective, true, ElementRef);
|
||||
$r3$.ɵviewQuery($e1_attrs$, true, ElementRef);
|
||||
$r3$.ɵviewQuery(SomeDirective, true, TemplateRef);
|
||||
}
|
||||
if (rf & 2) {
|
||||
var $tmp$;
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(0))) && (ctx.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(1))) && (ctx.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(2))) && (ctx.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵload(3))) && (ctx.someDirs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRef = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDir = $tmp$.first));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.myRefs = $tmp$));
|
||||
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadViewQuery())) && (ctx.someDirs = $tmp$));
|
||||
}
|
||||
},
|
||||
…
|
||||
@ -1554,8 +1554,8 @@ describe('compiler compliance', () => {
|
||||
return new (t || ContentQueryComponent)();
|
||||
},
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(SomeDirective, false), dirIndex);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
@ -1613,8 +1613,8 @@ describe('compiler compliance', () => {
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e0_attrs$, true), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery($e1_attrs$, false), dirIndex);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
@ -1666,10 +1666,10 @@ describe('compiler compliance', () => {
|
||||
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||
…
|
||||
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef), dirIndex);
|
||||
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef), 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);
|
||||
},
|
||||
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
|
||||
const instance = $r3$.ɵload(dirIndex);
|
||||
|
@ -709,9 +709,9 @@ describe('ngtsc behavioral tests', () => {
|
||||
|
||||
// Helper functions to construct RegExps for output validation
|
||||
const varRegExp = (name: string): RegExp => new RegExp(`var \\w+ = \\[\"${name}\"\\];`);
|
||||
const queryRegExp = (id: number | null, descend: boolean, ref?: string): RegExp => {
|
||||
const queryRegExp = (fnName: string, descend: boolean, ref?: string | null): RegExp => {
|
||||
const maybeRef = ref ? `, ${ref}` : ``;
|
||||
return new RegExp(`i0\\.ɵquery\\(${id}, \\w+, ${descend}${maybeRef}\\)`);
|
||||
return new RegExp(`i0\\.ɵ${fnName}\\(\\w+, ${descend}${maybeRef}\\)`);
|
||||
};
|
||||
|
||||
env.tsconfig();
|
||||
@ -740,13 +740,23 @@ describe('ngtsc behavioral tests', () => {
|
||||
expect(jsContents).toMatch(varRegExp('test1'));
|
||||
expect(jsContents).toMatch(varRegExp('test2'));
|
||||
expect(jsContents).toMatch(varRegExp('accessor'));
|
||||
expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, false)`);
|
||||
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, false)`);
|
||||
expect(jsContents)
|
||||
.toMatch(queryRegExp(
|
||||
null, true, 'TemplateRef')); // match `i0.ɵquery(null, _c0, true, TemplateRef)`
|
||||
expect(jsContents).toMatch(queryRegExp(null, true)); // match `i0.ɵquery(null, _c0, true)`
|
||||
expect(jsContents).toMatch(queryRegExp(0, true)); // match `i0.ɵquery(0, _c0, true)`
|
||||
expect(jsContents).toMatch(queryRegExp(1, true)); // match `i0.ɵquery(1, _c0, true)`
|
||||
.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));
|
||||
});
|
||||
|
||||
it('should handle queries that use forwardRef', () => {
|
||||
@ -767,8 +777,8 @@ describe('ngtsc behavioral tests', () => {
|
||||
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(`i0.ɵquery(null, TemplateRef, true)`);
|
||||
expect(jsContents).toContain(`i0.ɵquery(null, ViewContainerRef, true)`);
|
||||
expect(jsContents).toContain(`i0.ɵquery(TemplateRef, true)`);
|
||||
expect(jsContents).toContain(`i0.ɵquery(ViewContainerRef, true)`);
|
||||
});
|
||||
|
||||
it('should generate host listeners for components', () => {
|
||||
|
Reference in New Issue
Block a user