fix(ivy): query nodes from different TemplateRefs inserted into one ViewContainerRef (#24254)
PR Close #24254
This commit is contained in:
parent
5cbcb5680b
commit
0561b66a2b
|
@ -706,7 +706,7 @@ export function getOrCreateTemplateRef<T>(di: LInjector): viewEngine_TemplateRef
|
|||
ngDevMode && assertNotNull(hostTNode.tViews, 'TView must be allocated');
|
||||
di.templateRef = new TemplateRef<any>(
|
||||
getOrCreateElementRef(di), hostTNode.tViews as TView, hostNode.data.template !,
|
||||
getRenderer(), hostNode.queries);
|
||||
getRenderer(), hostNode.data.queries);
|
||||
}
|
||||
return di.templateRef;
|
||||
}
|
||||
|
|
|
@ -1514,16 +1514,20 @@ export function container(
|
|||
// Containers are added to the current view tree instead of their embedded views
|
||||
// because views can be removed and re-inserted.
|
||||
addToViewTree(currentView, index, node.data);
|
||||
|
||||
const queries = node.queries;
|
||||
if (queries) {
|
||||
// prepare place for matching nodes from views inserted into a given container
|
||||
lContainer.queries = queries.container();
|
||||
}
|
||||
|
||||
createDirectivesAndLocals(localRefs);
|
||||
|
||||
isParent = false;
|
||||
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
|
||||
const queries = node.queries;
|
||||
if (queries) {
|
||||
// check if a given container node matches
|
||||
queries.addNode(node);
|
||||
// prepare place for matching nodes from views inserted into a given container
|
||||
lContainer.queries = queries.container();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
*/
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
import {TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF} from '../../src/render3/di';
|
||||
import {QueryList, defineComponent, detectChanges} from '../../src/render3/index';
|
||||
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
||||
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectViewContainerRef} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {query, queryRefresh} from '../../src/render3/query';
|
||||
|
@ -18,6 +19,7 @@ import {NgForOf, NgIf} from './common_with_def';
|
|||
import {ComponentFixture, createComponent, createDirective, renderComponent} from './render_util';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to check if a given candidate object resembles ElementRef
|
||||
* @param candidate
|
||||
|
@ -690,6 +692,29 @@ describe('query', () => {
|
|||
|
||||
describe('ViewContainerRef', () => {
|
||||
|
||||
let directiveInstance: ViewContainerManipulatorDirective|null = null;
|
||||
|
||||
class ViewContainerManipulatorDirective {
|
||||
static ngDirectiveDef = defineDirective({
|
||||
type: ViewContainerManipulatorDirective,
|
||||
selectors: [['', 'vc', '']],
|
||||
factory: () => {
|
||||
return directiveInstance =
|
||||
new ViewContainerManipulatorDirective(injectViewContainerRef());
|
||||
}
|
||||
});
|
||||
|
||||
constructor(private _vcRef: ViewContainerRef) {}
|
||||
|
||||
insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) {
|
||||
this._vcRef.createEmbeddedView(tpl, ctx, idx);
|
||||
}
|
||||
|
||||
remove(index?: number) { this._vcRef.remove(index); }
|
||||
}
|
||||
|
||||
beforeEach(() => { directiveInstance = null; });
|
||||
|
||||
it('should report results in views inserted / removed by ngIf', () => {
|
||||
|
||||
/**
|
||||
|
@ -782,6 +807,97 @@ describe('query', () => {
|
|||
|
||||
});
|
||||
|
||||
// https://stackblitz.com/edit/angular-rrmmuf?file=src/app/app.component.ts
|
||||
it('should report results when different instances of TemplateRef are inserted into one ViewContainerRefs',
|
||||
() => {
|
||||
let tpl1: TemplateRef<{}>;
|
||||
let tpl2: TemplateRef<{}>;
|
||||
|
||||
|
||||
/**
|
||||
* <ng-template #tpl1 let-idx="idx">
|
||||
* <div #foo [id]="'foo1_'+idx"></div>
|
||||
* </ng-template>
|
||||
*
|
||||
* <div #foo id="middle"></div>
|
||||
*
|
||||
* <ng-template #tpl2 let-idx="idx">
|
||||
* <div #foo [id]="'foo2_'+idx"></div>
|
||||
* </ng-template>
|
||||
*
|
||||
* <ng-template viewInserter #vi="vi"></ng-template>
|
||||
*/
|
||||
const Cmpt = createComponent('cmpt', function(rf: RenderFlags, ctx: any) {
|
||||
let tmp: any;
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, ['foo'], true, QUERY_READ_FROM_NODE);
|
||||
|
||||
container(1, (rf: RenderFlags, ctx: {idx: number}) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div', null, ['foo', '']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'id', bind('foo1_' + ctx.idx));
|
||||
}
|
||||
}, null, []);
|
||||
|
||||
elementStart(2, 'div', ['id', 'middle'], ['foo', '']);
|
||||
elementEnd();
|
||||
|
||||
container(4, (rf: RenderFlags, ctx: {idx: number}) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div', null, ['foo', '']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'id', bind('foo2_' + ctx.idx));
|
||||
}
|
||||
}, null, []);
|
||||
|
||||
container(5, undefined, null, [AttributeMarker.SELECT_ONLY, 'vc']);
|
||||
}
|
||||
|
||||
if (rf & RenderFlags.Update) {
|
||||
tpl1 = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(1)));
|
||||
tpl2 = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(4)));
|
||||
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
|
||||
}, [ViewContainerManipulatorDirective]);
|
||||
|
||||
const fixture = new ComponentFixture(Cmpt);
|
||||
const qList = fixture.component.query;
|
||||
|
||||
expect(qList.length).toBe(1);
|
||||
expect(qList.first.nativeElement.getAttribute('id')).toBe('middle');
|
||||
|
||||
directiveInstance !.insertTpl(tpl1 !, {idx: 0}, 0);
|
||||
directiveInstance !.insertTpl(tpl2 !, {idx: 1}, 1);
|
||||
fixture.update();
|
||||
expect(qList.length).toBe(3);
|
||||
let qListArr = qList.toArray();
|
||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||
|
||||
directiveInstance !.insertTpl(tpl1 !, {idx: 1}, 1);
|
||||
fixture.update();
|
||||
expect(qList.length).toBe(4);
|
||||
qListArr = qList.toArray();
|
||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo1_1');
|
||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('middle');
|
||||
expect(qListArr[3].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||
|
||||
directiveInstance !.remove(1);
|
||||
fixture.update();
|
||||
expect(qList.length).toBe(3);
|
||||
qListArr = qList.toArray();
|
||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
|
||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
|
||||
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('JS blocks', () => {
|
||||
|
|
Loading…
Reference in New Issue