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:
Andrew Kushnir
2019-01-18 18:02:32 -08:00
committed by Jason Aden
parent 9f9024b7a1
commit 9098225ff0
21 changed files with 550 additions and 384 deletions

View File

@ -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);

View File

@ -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', () => {