test(ivy): move render3 query tests to acceptance (#30382)

Moves all manual render3 query tests that use the `elementProperty`
instructions to TestBed acceptance tests.

PR Close #30382
This commit is contained in:
Paul Gschwendtner 2019-05-10 14:07:45 +02:00 committed by Jason Aden
parent 1a0e500eea
commit 45e11915dc
2 changed files with 290 additions and 475 deletions

View File

@ -6,8 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren} from '@angular/core';
import {CommonModule} from '@angular/common';
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
@ -601,6 +603,44 @@ describe('query logic', () => {
expect(fixture.componentInstance.subComp.foo.length).toBe(2);
});
it('should match shallow content queries in views inserted / removed by ngIf', () => {
@Component({
selector: 'test-comp',
template: `
<shallow-comp>
<div *ngIf="showing" #foo></div>
</shallow-comp>
`
})
class TestComponent {
showing = false;
}
@Component({
selector: 'shallow-comp',
template: '',
})
class ShallowComp {
@ContentChildren('foo', {descendants: false}) foos !: QueryList<ElementRef>;
}
TestBed.configureTestingModule(
{declarations: [TestComponent, ShallowComp], imports: [CommonModule]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const shallowComp = fixture.debugElement.query(By.directive(ShallowComp)).componentInstance;
const queryList = shallowComp !.foos;
expect(queryList.length).toBe(0);
fixture.componentInstance.showing = true;
fixture.detectChanges();
expect(queryList.length).toBe(1);
fixture.componentInstance.showing = false;
fixture.detectChanges();
expect(queryList.length).toBe(0);
});
});
// Some root components may have ContentChildren queries if they are also
@ -665,6 +705,253 @@ describe('query logic', () => {
});
describe('view boundaries', () => {
describe('ViewContainerRef', () => {
@Directive({selector: '[vc]', exportAs: 'vc'})
class ViewContainerManipulatorDirective {
constructor(private _vcRef: ViewContainerRef) {}
insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) {
this._vcRef.createEmbeddedView(tpl, ctx, idx);
}
remove(index?: number) { this._vcRef.remove(index); }
}
it('should report results in views inserted / removed by ngIf', () => {
@Component({
selector: 'test-comp',
template: `
<ng-template [ngIf]="value">
<div #foo></div>
</ng-template>
`
})
class TestComponent {
value: boolean = false;
@ViewChildren('foo') query !: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const queryList = fixture.componentInstance.query;
expect(queryList.length).toBe(0);
fixture.componentInstance.value = true;
fixture.detectChanges();
expect(queryList.length).toBe(1);
fixture.componentInstance.value = false;
fixture.detectChanges();
expect(queryList.length).toBe(0);
});
it('should report results in views inserted / removed by ngFor', () => {
@Component({
selector: 'test-comp',
template: `
<ng-template ngFor let-item [ngForOf]="value">
<div #foo [id]="item"></div>
</ng-template>
`,
})
class TestComponent {
value: string[]|undefined;
@ViewChildren('foo') query !: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const queryList = fixture.componentInstance.query;
expect(queryList.length).toBe(0);
fixture.componentInstance.value = ['a', 'b', 'c'];
fixture.detectChanges();
expect(queryList.length).toBe(3);
// Remove the "b" element from the value.
fixture.componentInstance.value.splice(1, 1);
fixture.detectChanges();
expect(queryList.length).toBe(2);
// make sure that the "b" element has been removed from query results
expect(queryList.first.nativeElement.id).toBe('a');
expect(queryList.last.nativeElement.id).toBe('c');
});
// 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',
() => {
@Component({
selector: 'test-comp',
template: `
<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 vc></ng-template>
`,
})
class TestComponent {
@ViewChild(ViewContainerManipulatorDirective) vc !: ViewContainerManipulatorDirective;
@ViewChild('tpl1') tpl1 !: TemplateRef<any>;
@ViewChild('tpl2') tpl2 !: TemplateRef<any>;
@ViewChildren('foo') query !: QueryList<any>;
}
TestBed.configureTestingModule(
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const queryList = fixture.componentInstance.query;
const {tpl1, tpl2, vc} = fixture.componentInstance;
expect(queryList.length).toBe(1);
expect(queryList.first.nativeElement.getAttribute('id')).toBe('middle');
vc.insertTpl(tpl1 !, {idx: 0}, 0);
vc.insertTpl(tpl2 !, {idx: 1}, 1);
fixture.detectChanges();
expect(queryList.length).toBe(3);
let qListArr = queryList.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');
vc.insertTpl(tpl1 !, {idx: 1}, 1);
fixture.detectChanges();
expect(queryList.length).toBe(4);
qListArr = queryList.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');
vc.remove(1);
fixture.detectChanges();
expect(queryList.length).toBe(3);
qListArr = queryList.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');
vc.remove(1);
fixture.detectChanges();
expect(queryList.length).toBe(2);
qListArr = queryList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
});
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
// https://stackblitz.com/edit/angular-xzwp6n
onlyInIvy('FW-1318: QueryList entries are ordered differently in Ivy.')
.it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
() => {
@Component({
selector: 'test-comp',
template: `
<ng-template #tpl let-idx="idx" let-container_idx="container_idx">
<div #foo [id]="'foo_' + container_idx + '_' + idx"></div>
</ng-template>
<ng-template vc #vi0="vc"></ng-template>
<ng-template vc #vi1="vc"></ng-template>
`,
})
class TestComponent {
@ViewChild('tpl') tpl !: TemplateRef<any>;
@ViewChild('vi0') vi0 !: ViewContainerManipulatorDirective;
@ViewChild('vi1') vi1 !: ViewContainerManipulatorDirective;
@ViewChildren('foo') query !: QueryList<any>;
}
TestBed.configureTestingModule(
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
const queryList = fixture.componentInstance.query;
const {tpl, vi0, vi1} = fixture.componentInstance;
expect(queryList.length).toBe(0);
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
fixture.detectChanges();
expect(queryList.length).toBe(2);
let qListArr = queryList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_0_0');
vi0.remove();
fixture.detectChanges();
expect(queryList.length).toBe(1);
qListArr = queryList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
vi1.remove();
fixture.detectChanges();
expect(queryList.length).toBe(0);
});
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
it('should report results from views inserted in a lifecycle hook', () => {
@Component({
selector: 'my-app',
template: `
<ng-template #tpl>
<span #foo id="from_tpl"></span>
</ng-template>
<ng-template [ngTemplateOutlet]="show ? tpl : null"></ng-template>
`,
})
class MyApp {
show = false;
@ViewChildren('foo') query !: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
const queryList = fixture.componentInstance.query;
expect(queryList.length).toBe(0);
fixture.componentInstance.show = true;
fixture.detectChanges();
expect(queryList.length).toBe(1);
expect(queryList.first.nativeElement.id).toBe('from_tpl');
fixture.componentInstance.show = false;
fixture.detectChanges();
expect(queryList.length).toBe(0);
});
});
});
});
function initWithTemplate(compType: Type<any>, template: string) {

View File

@ -6,19 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NgForOfContext} from '@angular/common';
import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core';
import {EventEmitter} from '../..';
import {AttributeMarker, detectChanges, ΔProvidersFeature, ΔdefineComponent, ΔdefineDirective} from '../../src/render3/index';
import {Δbind, Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, ΔdirectiveInject, Δelement, ΔelementContainerEnd, ΔelementContainerStart, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δload, Δreference, Δtemplate, Δtext} from '../../src/render3/instructions/all';
import {Δcontainer, ΔcontainerRefreshEnd, ΔcontainerRefreshStart, ΔdirectiveInject, Δelement, ΔelementContainerEnd, ΔelementContainerStart, ΔelementEnd, ΔelementStart, ΔembeddedViewEnd, ΔembeddedViewStart, Δload, Δtemplate, Δtext} from '../../src/render3/instructions/all';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {query, ΔcontentQuery, ΔloadContentQuery, ΔloadViewQuery, ΔqueryRefresh, ΔviewQuery} from '../../src/render3/query';
import {getLView} from '../../src/render3/state';
import {getNativeByIndex} from '../../src/render3/util/view_utils';
import {ΔtemplateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
import {ComponentFixture, TemplateFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent} from './render_util';
@ -1346,412 +1344,6 @@ describe('query', () => {
describe('view boundaries', () => {
describe('ViewContainerRef', () => {
let directiveInstances: ViewContainerManipulatorDirective[] = [];
class ViewContainerManipulatorDirective {
static ngDirectiveDef = ΔdefineDirective({
type: ViewContainerManipulatorDirective,
selectors: [['', 'vc', '']],
factory: () => {
const directiveInstance =
new ViewContainerManipulatorDirective(ΔdirectiveInject(ViewContainerRef as any));
directiveInstances.push(directiveInstance);
return directiveInstance;
}
});
constructor(private _vcRef: ViewContainerRef) {}
insertTpl(tpl: TemplateRef<{}>, ctx: {}, idx?: number) {
this._vcRef.createEmbeddedView(tpl, ctx, idx);
}
remove(index?: number) { this._vcRef.remove(index); }
}
beforeEach(() => { directiveInstances = []; });
it('should report results in views inserted / removed by ngIf', () => {
function Cmpt_Template_1(rf: RenderFlags, ctx1: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
}
/**
* <ng-template [ngIf]="value">
* <div #foo></div>
* </ng-template>
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
const Cmpt = createComponent(
'cmpt',
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δtemplate(
0, Cmpt_Template_1, 2, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']);
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'ngIf', Δbind(ctx.value));
}
},
2, 1, [NgIf], [],
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔviewQuery(['foo'], true, null);
}
if (rf & RenderFlags.Update) {
let tmp: any;
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
(ctx.query = tmp as QueryList<any>);
}
});
const fixture = new ComponentFixture(Cmpt);
const qList = fixture.component.query;
expect(qList.length).toBe(0);
fixture.component.value = true;
fixture.update();
expect(qList.length).toBe(1);
fixture.component.value = false;
fixture.update();
expect(qList.length).toBe(0);
});
it('should report results in views inserted / removed by ngFor', () => {
function Cmpt_Template_1(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
if (rf1 & RenderFlags.Update) {
ΔelementProperty(0, 'id', Δbind(row.$implicit));
}
}
/**
* <ng-template ngFor let-item [ngForOf]="value">
* <div #foo [id]="item"></div>
* </ng-template>
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
class Cmpt {
// TODO(issue/24571): remove '!'.
value !: string[];
query: any;
static ngComponentDef = ΔdefineComponent({
type: Cmpt,
factory: () => new Cmpt(),
selectors: [['my-app']],
consts: 2,
vars: 1,
template: function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δtemplate(0, Cmpt_Template_1, 2, 1, 'ng-template', ['ngForOf', '']);
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'ngForOf', Δbind(ctx.value));
}
},
viewQuery: function(rf: RenderFlags, ctx: Cmpt) {
let tmp: any;
if (rf & RenderFlags.Create) {
ΔviewQuery(['foo'], true, null);
}
if (rf & RenderFlags.Update) {
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
(ctx.query = tmp as QueryList<any>);
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(Cmpt);
const qList = fixture.component.query;
expect(qList.length).toBe(0);
fixture.component.value = ['a', 'b', 'c'];
fixture.update();
expect(qList.length).toBe(3);
fixture.component.value.splice(1, 1); // remove "b"
fixture.update();
expect(qList.length).toBe(2);
// make sure that a proper element was removed from query results
expect(qList.first.nativeElement.id).toBe('a');
expect(qList.last.nativeElement.id).toBe('c');
});
// 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<{}>;
function Cmpt_Template_1(rf: RenderFlags, ctx: {idx: number}) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'id', Δbind('foo1_' + ctx.idx));
}
}
function Cmpt_Template_5(rf: RenderFlags, ctx: {idx: number}) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'id', Δbind('foo2_' + ctx.idx));
}
}
/**
* <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) {
if (rf & RenderFlags.Create) {
Δtemplate(
0, Cmpt_Template_1, 2, 1, 'ng-template', null, ['tpl1', ''],
ΔtemplateRefExtractor);
Δelement(2, 'div', ['id', 'middle'], ['foo', '']);
Δtemplate(
4, Cmpt_Template_5, 2, 1, 'ng-template', null, ['tpl2', ''],
ΔtemplateRefExtractor);
Δtemplate(6, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
}
if (rf & RenderFlags.Update) {
tpl1 = Δreference(1);
tpl2 = Δreference(5);
}
},
8, 0, [ViewContainerManipulatorDirective], [],
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔviewQuery(['foo'], true, null);
}
if (rf & RenderFlags.Update) {
let tmp: any;
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
(ctx.query = tmp as QueryList<any>);
}
});
const fixture = new ComponentFixture(Cmpt);
const qList = fixture.component.query;
expect(qList.length).toBe(1);
expect(qList.first.nativeElement.getAttribute('id')).toBe('middle');
directiveInstances[0].insertTpl(tpl1 !, {idx: 0}, 0);
directiveInstances[0].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');
directiveInstances[0].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');
directiveInstances[0].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');
directiveInstances[0].remove(1);
fixture.update();
expect(qList.length).toBe(2);
qListArr = qList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo1_0');
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
});
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
// https://stackblitz.com/edit/angular-xzwp6n
it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
() => {
let tpl: TemplateRef<{}>;
function Cmpt_Template_1(rf: RenderFlags, ctx: {idx: number, container_idx: number}) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
if (rf & RenderFlags.Update) {
ΔelementProperty(0, 'id', Δbind('foo_' + ctx.container_idx + '_' + ctx.idx));
}
}
/**
* <ng-template #tpl let-idx="idx" let-container_idx="container_idx">
* <div #foo [id]="'foo_'+container_idx+'_'+idx"></div>
* </ng-template>
*
* <ng-template viewInserter #vi1="vi"></ng-template>
* <ng-template viewInserter #vi2="vi"></ng-template>
*/
class Cmpt {
query: any;
static ngComponentDef = ΔdefineComponent({
type: Cmpt,
factory: () => new Cmpt(),
selectors: [['my-app']],
consts: 4,
vars: 0,
template: function(rf: RenderFlags, ctx: any) {
let tmp: any;
if (rf & RenderFlags.Create) {
Δtemplate(
0, Cmpt_Template_1, 2, 1, 'ng-template', [], ['tpl', ''],
ΔtemplateRefExtractor);
Δtemplate(2, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
Δtemplate(3, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'vc']);
}
if (rf & RenderFlags.Update) {
tpl = Δreference(1);
}
},
viewQuery: (rf: RenderFlags, cmpt: Cmpt) => {
let tmp: any;
if (rf & RenderFlags.Create) {
ΔviewQuery(['foo'], true, null);
}
if (rf & RenderFlags.Update) {
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
(cmpt.query = tmp as QueryList<any>);
}
},
directives: () => [ViewContainerManipulatorDirective],
});
}
const fixture = new ComponentFixture(Cmpt);
const qList = fixture.component.query;
expect(qList.length).toBe(0);
directiveInstances[0].insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
directiveInstances[1].insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
fixture.update();
expect(qList.length).toBe(2);
let qListArr = qList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_0_0');
directiveInstances[0].remove();
fixture.update();
expect(qList.length).toBe(1);
qListArr = qList.toArray();
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
directiveInstances[1].remove();
fixture.update();
expect(qList.length).toBe(0);
});
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
it('should report results from views inserted in a lifecycle hook', () => {
function MyApp_Template_1(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'span', ['id', 'from_tpl'], ['foo', '']);
}
}
class MyApp {
show = false;
query: any;
static ngComponentDef = ΔdefineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
consts: 4,
vars: 1,
/**
* <ng-template #tpl><span #foo id="from_tpl"></span></ng-template>
* <ng-template [ngTemplateOutlet]="show ? tpl : null"></ng-template>
*/
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
Δtemplate(
0, MyApp_Template_1, 2, 0, 'ng-template', undefined, ['tpl', ''],
ΔtemplateRefExtractor);
Δtemplate(
2, null, 0, 0, 'ng-template', [AttributeMarker.Bindings, 'ngTemplateOutlet']);
}
if (rf & RenderFlags.Update) {
const tplRef = Δreference(1);
ΔelementProperty(2, 'ngTemplateOutlet', Δbind(myApp.show ? tplRef : null));
}
},
directives: () => [NgTemplateOutlet],
viewQuery: (rf: RenderFlags, myApp: MyApp) => {
let tmp: any;
if (rf & RenderFlags.Create) {
ΔviewQuery(['foo'], true, null);
}
if (rf & RenderFlags.Update) {
ΔqueryRefresh(tmp = ΔloadViewQuery<QueryList<any>>()) &&
(myApp.query = tmp as QueryList<any>);
}
}
});
}
const fixture = new ComponentFixture(MyApp);
const qList = fixture.component.query;
expect(qList.length).toBe(0);
fixture.component.show = true;
fixture.update();
expect(qList.length).toBe(1);
expect(qList.first.nativeElement.id).toBe('from_tpl');
fixture.component.show = false;
fixture.update();
expect(qList.length).toBe(0);
});
});
describe('JS blocks', () => {
it('should report results in embedded views', () => {
@ -2233,12 +1825,8 @@ describe('query', () => {
describe('content', () => {
let withContentInstance: WithContentDirective|null;
let shallowCompInstance: ShallowComp|null;
beforeEach(() => {
withContentInstance = null;
shallowCompInstance = null;
});
beforeEach(() => { withContentInstance = null; });
class WithContentDirective {
// @ContentChildren('foo')
@ -2268,29 +1856,6 @@ describe('query', () => {
});
}
class ShallowComp {
// @ContentChildren('foo', {descendants: false})
foos !: QueryList<ElementRef>;
static ngComponentDef = ΔdefineComponent({
type: ShallowComp,
selectors: [['shallow-comp']],
factory: () => shallowCompInstance = new ShallowComp(),
template: function(rf: RenderFlags, ctx: any) {},
consts: 0,
vars: 0,
contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => {
if (rf & RenderFlags.Create) {
ΔcontentQuery(dirIndex, ['foo'], false, null);
}
if (rf & RenderFlags.Update) {
let tmp: any;
ΔqueryRefresh(tmp = ΔloadContentQuery<ElementRef>()) && (ctx.foos = tmp);
}
}
});
}
it('should support content queries for directives', () => {
/**
* <div with-content>
@ -2381,43 +1946,6 @@ describe('query', () => {
.toBe(0, `Expected content query not to match <div with-content #foo>.`);
});
it('should match shallow content queries in views inserted / removed by ngIf', () => {
function IfTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
Δelement(0, 'div', null, ['foo', '']);
}
}
/**
* <shallow-comp>
* <div *ngIf="showing" #foo></div>
* </shallow-comp>
*/
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
ΔelementStart(0, 'shallow-comp');
{ Δtemplate(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Template, 'ngIf', '']); }
ΔelementEnd();
}
if (rf & RenderFlags.Update) {
ΔelementProperty(1, 'ngIf', Δbind(ctx.showing));
}
}, 2, 1, [ShallowComp, NgIf]);
const fixture = new ComponentFixture(AppComponent);
const qList = shallowCompInstance !.foos;
expect(qList.length).toBe(0);
fixture.component.showing = true;
fixture.update();
expect(qList.length).toBe(1);
fixture.component.showing = false;
fixture.update();
expect(qList.length).toBe(0);
});
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
it('should support view and content queries matching the same element', () => {
/**