fix(ivy): save queries at the correct indices (#28327)
Previous to this change, we were storing view queries at the wrong index. This is because we were passing a raw index to the store() instruction instead of an adjusted index (i.e. an index that does not include the HEADER_OFFSET). We had an additional issue where TView.blueprint was not backfilled when TView.data was backfilled, so new component instances created from the blueprint would end up having a shorter LView. Both of these problems together led to the Material demo app failing with Ivy. This commit fixes those discrepancies. PR Close #28327
This commit is contained in:
parent
d4ecffe475
commit
c1fb9c265c
|
@ -2930,6 +2930,7 @@ export function store<T>(index: number, value: T): void {
|
|||
const adjustedIndex = index + HEADER_OFFSET;
|
||||
if (adjustedIndex >= tView.data.length) {
|
||||
tView.data[adjustedIndex] = null;
|
||||
tView.blueprint[adjustedIndex] = null;
|
||||
}
|
||||
lView[adjustedIndex] = value;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'
|
|||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||
import {LView, TVIEW} from './interfaces/view';
|
||||
import {HEADER_OFFSET, LView, TVIEW} from './interfaces/view';
|
||||
import {getCurrentViewQueryIndex, getIsParent, getLView, getOrCreateCurrentQueries, setCurrentViewQueryIndex} from './state';
|
||||
import {isContentQueryHost} from './util';
|
||||
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
|
||||
|
@ -407,7 +407,7 @@ export function viewQuery<T>(
|
|||
}
|
||||
const index = getCurrentViewQueryIndex();
|
||||
const viewQuery: QueryList<T> = query<T>(predicate, descend, read);
|
||||
store(index, viewQuery);
|
||||
store(index - HEADER_OFFSET, viewQuery);
|
||||
setCurrentViewQueryIndex(index + 1);
|
||||
return viewQuery;
|
||||
}
|
||||
|
@ -418,5 +418,5 @@ export function viewQuery<T>(
|
|||
export function loadViewQuery<T>(): T {
|
||||
const index = getCurrentViewQueryIndex();
|
||||
setCurrentViewQueryIndex(index + 1);
|
||||
return load<T>(index);
|
||||
return load<T>(index - HEADER_OFFSET);
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
import {Inject, InjectionToken, QueryList} from '../../src/core';
|
||||
import {ComponentDef, DirectiveDef, InheritDefinitionFeature, NgOnChangesFeature, ProvidersFeature, RenderFlags, allocHostVars, bind, defineBase, defineComponent, defineDirective, directiveInject, element, elementProperty, loadViewQuery, queryRefresh, viewQuery} from '../../src/render3/index';
|
||||
|
||||
import {ComponentFixture, createComponent} from './render_util';
|
||||
import {ComponentFixture, createComponent, getDirectiveOnNode} from './render_util';
|
||||
|
||||
describe('InheritDefinitionFeature', () => {
|
||||
it('should inherit lifecycle hooks', () => {
|
||||
|
@ -363,6 +363,76 @@ describe('InheritDefinitionFeature', () => {
|
|||
expect(divEl.title).toEqual('new-title');
|
||||
});
|
||||
|
||||
describe('view query', () => {
|
||||
|
||||
const SomeComp = createComponent('some-comp', (rf: RenderFlags, ctx: any) => {});
|
||||
|
||||
/*
|
||||
* class SuperComponent {
|
||||
* @ViewChildren('super') superQuery;
|
||||
* }
|
||||
*/
|
||||
class SuperComponent {
|
||||
superQuery?: QueryList<any>;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SuperComponent,
|
||||
template: () => {},
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
selectors: [['super-comp']],
|
||||
viewQuery: <T>(rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(['super'], false);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||
(ctx.superQuery = tmp as QueryList<any>);
|
||||
}
|
||||
},
|
||||
factory: () => new SuperComponent(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <div id="sub" #sub></div>
|
||||
* <div id="super" #super></div>
|
||||
* <some-comp></some-comp>
|
||||
* class SubComponent extends SuperComponent {
|
||||
* @ViewChildren('sub') subQuery;
|
||||
* }
|
||||
*/
|
||||
class SubComponent extends SuperComponent {
|
||||
subQuery?: QueryList<any>;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SubComponent,
|
||||
template: (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'div', ['id', 'sub'], ['sub', '']);
|
||||
element(2, 'div', ['id', 'super'], ['super', '']);
|
||||
element(4, 'some-comp');
|
||||
}
|
||||
},
|
||||
consts: 5,
|
||||
vars: 0,
|
||||
selectors: [['sub-comp']],
|
||||
viewQuery: (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(['sub'], false);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||
(ctx.subQuery = tmp as QueryList<any>);
|
||||
}
|
||||
},
|
||||
factory: () => new SubComponent(),
|
||||
features: [InheritDefinitionFeature],
|
||||
directives: [SomeComp]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
it('should compose viewQuery (basic mechanics check)', () => {
|
||||
const log: Array<[string, RenderFlags, any]> = [];
|
||||
|
||||
|
@ -404,69 +474,9 @@ describe('InheritDefinitionFeature', () => {
|
|||
expect(log).toEqual([['super', 1, context], ['sub', 1, context]]);
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should compose viewQuery (query logic check)', () => {
|
||||
/*
|
||||
* class SuperComponent {
|
||||
* @ViewChildren('super') superQuery;
|
||||
* }
|
||||
*/
|
||||
class SuperComponent {
|
||||
superQuery?: QueryList<any>;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SuperComponent,
|
||||
template: () => {},
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
selectors: [['', 'superDir', '']],
|
||||
viewQuery: <T>(rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(['super'], false);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||
(ctx.superQuery = tmp as QueryList<any>);
|
||||
}
|
||||
},
|
||||
factory: () => new SuperComponent(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <div id="sub" #sub></div>
|
||||
* <div id="super" #super></div>
|
||||
* class SubComponent extends SuperComponent {
|
||||
* @ViewChildren('sub') subQuery;
|
||||
* }
|
||||
*/
|
||||
class SubComponent extends SuperComponent {
|
||||
subQuery?: QueryList<any>;
|
||||
static ngComponentDef = defineComponent({
|
||||
type: SubComponent,
|
||||
template: (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'div', ['id', 'sub'], ['sub', '']);
|
||||
element(2, 'div', ['id', 'super'], ['super', '']);
|
||||
}
|
||||
},
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
selectors: [['', 'subDir', '']],
|
||||
viewQuery: (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(['sub'], false);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = loadViewQuery<QueryList<any>>()) &&
|
||||
(ctx.subQuery = tmp as QueryList<any>);
|
||||
}
|
||||
},
|
||||
factory: () => new SubComponent(),
|
||||
features: [InheritDefinitionFeature]
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(SubComponent);
|
||||
|
||||
const check = (key: string): void => {
|
||||
|
@ -480,6 +490,35 @@ describe('InheritDefinitionFeature', () => {
|
|||
check('super');
|
||||
});
|
||||
|
||||
it('should work with multiple viewQuery comps', () => {
|
||||
let subCompOne !: SubComponent;
|
||||
let subCompTwo !: SubComponent;
|
||||
|
||||
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(0, 'sub-comp');
|
||||
element(1, 'sub-comp');
|
||||
}
|
||||
subCompOne = getDirectiveOnNode(0);
|
||||
subCompTwo = getDirectiveOnNode(1);
|
||||
}, 2, 0, [SubComponent, SuperComponent]);
|
||||
|
||||
const fixture = new ComponentFixture(App);
|
||||
|
||||
const check = (comp: SubComponent): void => {
|
||||
const qList = comp.subQuery as QueryList<any>;
|
||||
expect(qList.length).toBe(1);
|
||||
expect(qList.first.nativeElement).toEqual(fixture.hostElement.querySelector('#sub'));
|
||||
expect(qList.first.nativeElement.id).toEqual('sub');
|
||||
};
|
||||
|
||||
check(subCompOne);
|
||||
check(subCompTwo);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should compose contentQueries', () => {
|
||||
const log: string[] = [];
|
||||
|
||||
|
|
|
@ -1305,14 +1305,14 @@ describe('query', () => {
|
|||
0, Cmpt_Template_1, 2, 0, 'ng-template', null, ['foo', ''],
|
||||
templateRefExtractor);
|
||||
template(
|
||||
1, Cmpt_Template_1, 2, 0, 'ng-template', null, ['bar', ''],
|
||||
2, Cmpt_Template_1, 2, 0, 'ng-template', null, ['bar', ''],
|
||||
templateRefExtractor);
|
||||
template(
|
||||
2, Cmpt_Template_1, 2, 0, 'ng-template', null, ['baz', ''],
|
||||
4, Cmpt_Template_1, 2, 0, 'ng-template', null, ['baz', ''],
|
||||
templateRefExtractor);
|
||||
}
|
||||
},
|
||||
3, 0, [], [],
|
||||
6, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(TemplateRef as any, false);
|
||||
|
@ -2160,7 +2160,7 @@ describe('query', () => {
|
|||
element(0, 'div', null, ['foo', '']);
|
||||
}
|
||||
},
|
||||
1, 0, [], [],
|
||||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
viewQuery(['foo'], false);
|
||||
|
|
|
@ -279,8 +279,8 @@ class SuperComp {
|
|||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: SuperComp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(1, 'div');
|
||||
element(2, 'child-comp', ['child', ''], ['child', 'child']);
|
||||
elementStart(0, 'div');
|
||||
element(1, 'child-comp', ['child', ''], ['child', 'child']);
|
||||
elementEnd();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2060,10 +2060,10 @@ describe('ViewContainerRef', () => {
|
|||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: DynamicCompWithViewQueries) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div', ['bar', ''], ['foo', '']);
|
||||
element(0, 'div', ['bar', ''], ['foo', '']);
|
||||
}
|
||||
// testing only
|
||||
fooEl = getNativeByIndex(1, getLView());
|
||||
fooEl = getNativeByIndex(0, getLView());
|
||||
},
|
||||
viewQuery: function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
|
|
Loading…
Reference in New Issue