fix(ivy): restore missing match operation in View and Content Queries (#26847)
PR Close #26847
This commit is contained in:
parent
ff2ee644b4
commit
3567e81175
|
@ -269,7 +269,7 @@ function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: T
|
|||
}
|
||||
|
||||
// TODO: "read" should be an AbstractType (FW-486)
|
||||
function queryRead(tNode: TNode, currentView: LViewData, read: any): any {
|
||||
function queryByReadToken(read: any, tNode: TNode, currentView: LViewData): any {
|
||||
const factoryFn = (read as any)[NG_ELEMENT_ID];
|
||||
if (typeof factoryFn === 'function') {
|
||||
return factoryFn();
|
||||
|
@ -282,7 +282,7 @@ function queryRead(tNode: TNode, currentView: LViewData, read: any): any {
|
|||
return null;
|
||||
}
|
||||
|
||||
function queryReadByTNodeType(tNode: TNode, currentView: LViewData): any {
|
||||
function queryByTNodeType(tNode: TNode, currentView: LViewData): any {
|
||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
||||
return createElementRef(ViewEngine_ElementRef, tNode, currentView);
|
||||
}
|
||||
|
@ -292,37 +292,54 @@ function queryReadByTNodeType(tNode: TNode, currentView: LViewData): any {
|
|||
return null;
|
||||
}
|
||||
|
||||
function queryByTemplateRef(
|
||||
templateRefToken: ViewEngine_TemplateRef<any>, tNode: TNode, currentView: LViewData,
|
||||
read: any): any {
|
||||
const templateRefResult = (templateRefToken as any)[NG_ELEMENT_ID]();
|
||||
if (read) {
|
||||
return templateRefResult ? queryByReadToken(read, tNode, currentView) : null;
|
||||
}
|
||||
return templateRefResult;
|
||||
}
|
||||
|
||||
function queryRead(tNode: TNode, currentView: LViewData, read: any, matchingIdx: number): any {
|
||||
if (read) {
|
||||
return queryByReadToken(read, tNode, currentView);
|
||||
}
|
||||
if (matchingIdx > -1) {
|
||||
return currentView[matchingIdx];
|
||||
}
|
||||
// if read token and / or strategy is not specified,
|
||||
// detect it using appropriate tNode type
|
||||
return queryByTNodeType(tNode, currentView);
|
||||
}
|
||||
|
||||
function add(
|
||||
query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode) {
|
||||
const currentView = getViewData();
|
||||
|
||||
while (query) {
|
||||
const predicate = query.predicate;
|
||||
const type = predicate.type;
|
||||
const type = predicate.type as any;
|
||||
if (type) {
|
||||
// if read token and / or strategy is not specified, use type as read token
|
||||
const result = queryRead(tNode, currentView, predicate.read || type);
|
||||
let result = null;
|
||||
if (type === ViewEngine_TemplateRef) {
|
||||
result = queryByTemplateRef(type, tNode, currentView, predicate.read);
|
||||
} else {
|
||||
const matchingIdx = getIdxOfMatchingDirective(tNode, currentView, type);
|
||||
if (matchingIdx !== null) {
|
||||
result = queryRead(tNode, currentView, predicate.read, matchingIdx);
|
||||
}
|
||||
}
|
||||
if (result !== null) {
|
||||
addMatch(query, result);
|
||||
}
|
||||
} else {
|
||||
const selector = predicate.selector !;
|
||||
for (let i = 0; i < selector.length; i++) {
|
||||
const directiveIdx = getIdxOfMatchingSelector(tNode, selector[i]);
|
||||
if (directiveIdx !== null) {
|
||||
let result: any = null;
|
||||
if (predicate.read) {
|
||||
result = queryRead(tNode, currentView, predicate.read);
|
||||
} else {
|
||||
if (directiveIdx > -1) {
|
||||
result = currentView[directiveIdx];
|
||||
} else {
|
||||
// if read token and / or strategy is not specified,
|
||||
// detect it using appropriate tNode type
|
||||
result = queryReadByTNodeType(tNode, currentView);
|
||||
}
|
||||
}
|
||||
|
||||
const matchingIdx = getIdxOfMatchingSelector(tNode, selector[i]);
|
||||
if (matchingIdx !== null) {
|
||||
const result = queryRead(tNode, currentView, predicate.read, matchingIdx);
|
||||
if (result !== null) {
|
||||
addMatch(query, result);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import {EventEmitter} from '../..';
|
|||
|
||||
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index';
|
||||
import {getNativeByIndex} from '../../src/render3/util';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template, text} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {query, queryRefresh} from '../../src/render3/query';
|
||||
import {getViewData} from '../../src/render3/state';
|
||||
|
@ -949,7 +949,7 @@ describe('query', () => {
|
|||
expect(qList.last).toBe(childInstance);
|
||||
});
|
||||
|
||||
it('should not add results to query if a requested token cant be read', () => {
|
||||
it('should not add results to selector-based query if a requested token cant be read', () => {
|
||||
const Child = createDirective('child');
|
||||
|
||||
/**
|
||||
|
@ -976,11 +976,226 @@ describe('query', () => {
|
|||
}
|
||||
});
|
||||
|
||||
const cmptInstance = renderComponent(Cmpt);
|
||||
const qList = (cmptInstance.query as QueryList<any>);
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not add results to directive-based query if requested token cant be read', () => {
|
||||
const Child = createDirective('child');
|
||||
const OtherChild = createDirective('otherchild');
|
||||
|
||||
/**
|
||||
* <div child></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren(Child, {read: OtherChild}) query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div', ['child', '']);
|
||||
}
|
||||
},
|
||||
2, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, Child, false, OtherChild);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not add results to directive-based query if only read token matches', () => {
|
||||
const Child = createDirective('child');
|
||||
const OtherChild = createDirective('otherchild');
|
||||
|
||||
/**
|
||||
* <div child></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren(OtherChild, {read: Child}) query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div', ['child', '']);
|
||||
}
|
||||
},
|
||||
2, 0, [Child, OtherChild], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, OtherChild, false, Child);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not add results to TemplateRef-based query if only read token matches', () => {
|
||||
/**
|
||||
* <div></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren(TemplateRef, {read: ElementRef}) query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div');
|
||||
}
|
||||
},
|
||||
2, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, TemplateRef as any, false, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should match using string selector and directive as a read argument', () => {
|
||||
const Child = createDirective('child');
|
||||
|
||||
/**
|
||||
* <div child #foo></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo', {read: Child}) query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div', ['child', ''], ['foo', '']);
|
||||
}
|
||||
},
|
||||
3, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, ['foo'], false, Child);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(1);
|
||||
expect(qList.first instanceof Child).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not add results to the query in case no match found (via TemplateRef)', () => {
|
||||
const Child = createDirective('child');
|
||||
|
||||
/**
|
||||
* <div child></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren(TemplateRef) query;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
element(1, 'div', ['child', '']);
|
||||
}
|
||||
},
|
||||
2, 0, [Child], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, TemplateRef as any, false);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
const qList = component.query;
|
||||
expect(qList.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should query templates if the type is TemplateRef (and respect "read" option)', () => {
|
||||
function Cmpt_Template_1(rf: RenderFlags, ctx1: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
text(1, 'Test');
|
||||
elementEnd();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <ng-template #foo><div>Test</div></ng-template>
|
||||
* <ng-template #bar><div>Test</div></ng-template>
|
||||
* <ng-template #baz><div>Test</div></ng-template>
|
||||
* class Cmpt {
|
||||
* @ViewChildren(TemplateRef) tmplQuery;
|
||||
* @ViewChildren(TemplateRef, {read: ElementRef}) elemQuery;
|
||||
* }
|
||||
*/
|
||||
const Cmpt = createComponent(
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
template(2, Cmpt_Template_1, 2, 0, null, null, ['foo', ''], templateRefExtractor);
|
||||
template(3, Cmpt_Template_1, 2, 0, null, null, ['bar', ''], templateRefExtractor);
|
||||
template(4, Cmpt_Template_1, 2, 0, null, null, ['baz', ''], templateRefExtractor);
|
||||
}
|
||||
},
|
||||
5, 0, [], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, TemplateRef as any, false);
|
||||
query(1, TemplateRef as any, false, ElementRef);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) &&
|
||||
(ctx.tmplQuery = tmp as QueryList<any>);
|
||||
queryRefresh(tmp = load<QueryList<any>>(1)) &&
|
||||
(ctx.elemQuery = tmp as QueryList<any>);
|
||||
}
|
||||
});
|
||||
|
||||
const {component} = new ComponentFixture(Cmpt);
|
||||
|
||||
// check template-based query set
|
||||
const tmplQList = component.tmplQuery;
|
||||
expect(tmplQList.length).toBe(3);
|
||||
expect(isTemplateRef(tmplQList.first)).toBeTruthy();
|
||||
|
||||
// check element-based query set
|
||||
const elemQList = component.elemQuery;
|
||||
expect(elemQList.length).toBe(3);
|
||||
expect(isElementRef(elemQList.first)).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue