/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {C, D, E, Q, QueryList, c, e, m, qR} from '../../src/render3/index';
import {QueryReadType} from '../../src/render3/interfaces';
import {createComponent, createDirective, renderComponent} from './render_util';
/**
* Helper function to check if a given candidate object resembles ElementRef
* @param candidate
* @returns true if `ElementRef`.
*/
function isElementRef(candidate: any): boolean {
return candidate.nativeElement != null;
}
/**
* Helper function to check if a given candidate object resembles TemplateRef
* @param candidate
* @returns true if `TemplateRef`.
*/
function isTemplateRef(candidate: any): boolean {
return candidate.createEmbeddedView != null && candidate.createComponent == null;
}
/**
* Helper function to check if a given candidate object resembles ViewContainerRef
* @param candidate
* @returns true if `ViewContainerRef`.
*/
function isViewContainerRef(candidate: any): boolean {
return candidate.createEmbeddedView != null && candidate.createComponent != null;
}
describe('query', () => {
it('should project query children', () => {
const Child = createComponent('child', function(ctx: any, cm: boolean) {});
let child1 = null;
let child2 = null;
const Cmp = createComponent('cmp', function(ctx: any, cm: boolean) {
/**
*
*
*
*
* class Cmp {
* @ViewChildren(Child) query0;
* @ViewChildren(Child, {descend: true}) query1;
* }
*/
let tmp: any;
if (cm) {
m(0, Q(Child, false));
m(1, Q(Child, true));
E(2, Child.ngComponentDef);
{
child1 = D(3, Child.ngComponentDef.n(), Child.ngComponentDef);
E(4, Child.ngComponentDef);
{ child2 = D(5, Child.ngComponentDef.n(), Child.ngComponentDef); }
e();
}
e();
}
qR(tmp = m>(0)) && (ctx.query0 = tmp as QueryList);
qR(tmp = m>(1)) && (ctx.query1 = tmp as QueryList);
});
const parent = renderComponent(Cmp);
expect((parent.query0 as QueryList).toArray()).toEqual([child1]);
expect((parent.query1 as QueryList).toArray()).toEqual([child1, child2]);
});
describe('types predicate', () => {
it('should query using type predicate and read a specified token', () => {
const Child = createDirective();
let elToQuery;
/**
*
* class Cmpt {
* @ViewChildren(Child, {read: ElementRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(Child, false, QueryReadType.ElementRef));
elToQuery = E(1, 'div');
{ D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isElementRef(query.first)).toBeTruthy();
expect(query.first.nativeElement).toEqual(elToQuery);
});
it('should query using type predicate and read another directive type', () => {
const Child = createDirective();
const OtherChild = createDirective();
let otherChildInstance;
/**
*
* class Cmpt {
* @ViewChildren(Child, {read: OtherChild}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(Child, false, OtherChild));
E(1, 'div');
{
D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef);
D(3, otherChildInstance = OtherChild.ngDirectiveDef.n(), OtherChild.ngDirectiveDef);
}
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(query.first).toBe(otherChildInstance);
});
it('should not add results to query if a requested token cant be read', () => {
const Child = createDirective();
const OtherChild = createDirective();
/**
*
* class Cmpt {
* @ViewChildren(Child, {read: OtherChild}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(Child, false, OtherChild));
E(1, 'div');
{ D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(0);
});
});
describe('local names predicate', () => {
it('should query for a single element and read ElementRef', () => {
let elToQuery;
/**
*
*
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo']));
elToQuery = E(1, 'div', [], 'foo');
e();
E(2, 'div');
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(query.first.nativeElement).toEqual(elToQuery);
});
it('should query for multiple elements and read ElementRef', () => {
let el1ToQuery;
let el2ToQuery;
/**
*
*
*
* class Cmpt {
* @ViewChildren('foo,bar') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo', 'bar']));
el1ToQuery = E(1, 'div', null, 'foo');
e();
E(2, 'div');
e();
el2ToQuery = E(3, 'div', null, 'bar');
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(2);
expect(query.first.nativeElement).toEqual(el1ToQuery);
expect(query.last.nativeElement).toEqual(el2ToQuery);
});
it('should read ElementRef from an element when explicitly asked for', () => {
let elToQuery;
/**
*
*
* class Cmpt {
* @ViewChildren('foo', {read: ElementRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, QueryReadType.ElementRef));
elToQuery = E(1, 'div', [], 'foo');
e();
E(2, 'div');
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isElementRef(query.first)).toBeTruthy();
expect(query.first.nativeElement).toEqual(elToQuery);
});
it('should read ViewContainerRef from element nodes when explicitly asked for', () => {
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: ViewContainerRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, QueryReadType.ViewContainerRef));
E(1, 'div', [], 'foo');
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isViewContainerRef(query.first)).toBeTruthy();
});
it('should read ViewContainerRef from container nodes when explicitly asked for', () => {
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: ViewContainerRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, QueryReadType.ViewContainerRef));
C(1, undefined, undefined, undefined, 'foo');
c();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isViewContainerRef(query.first)).toBeTruthy();
});
it('should read ElementRef with a native element pointing to comment DOM node from containers',
() => {
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: ElementRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, QueryReadType.ElementRef));
C(1, undefined, undefined, undefined, 'foo');
c();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isElementRef(query.first)).toBeTruthy();
expect(query.first.nativeElement.nodeType).toBe(8); // Node.COMMENT_NODE = 8
});
it('should read TemplateRef from container nodes by default', () => {
// http://plnkr.co/edit/BVpORly8wped9I3xUYsX?p=preview
/**
*
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo']));
C(1, undefined, undefined, undefined, 'foo');
c();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isTemplateRef(query.first)).toBeTruthy();
});
it('should read TemplateRef from container nodes when explicitly asked for', () => {
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: TemplateRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, QueryReadType.TemplateRef));
C(1, undefined, undefined, undefined, 'foo');
c();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(isTemplateRef(query.first)).toBeTruthy();
});
it('should read component instance if element queried for is a component host', () => {
const Child = createComponent('child', function(ctx: any, cm: boolean) {});
let childInstance;
/**
*
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo']));
E(1, Child.ngComponentDef, []);
{ childInstance = D(2, Child.ngComponentDef.n(), Child.ngComponentDef, 'foo'); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(query.first).toBe(childInstance);
});
it('should read directive instance if element queried for has an exported directive with a matching name',
() => {
const Child = createDirective();
let childInstance;
/**
*
* class Cmpt {
* @ViewChildren('foo') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo']));
E(1, 'div');
{ childInstance = D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef, 'foo'); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(query.first).toBe(childInstance);
});
it('should read all matching directive instances from a given element', () => {
const Child1 = createDirective();
const Child2 = createDirective();
let child1Instance, child2Instance;
/**
*
* class Cmpt {
* @ViewChildren('foo, bar') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo', 'bar']));
E(1, 'div');
{
child1Instance = D(2, Child1.ngDirectiveDef.n(), Child1.ngDirectiveDef, 'foo');
child2Instance = D(3, Child2.ngDirectiveDef.n(), Child2.ngDirectiveDef, 'bar');
}
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(2);
expect(query.first).toBe(child1Instance);
expect(query.last).toBe(child2Instance);
});
it('should match match on exported directive name and read a requested token', () => {
const Child = createDirective();
let div;
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: ElementRef}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], undefined, QueryReadType.ElementRef));
div = E(1, 'div');
{ D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef, 'foo'); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(1);
expect(query.first.nativeElement).toBe(div);
});
it('should support reading a mix of ElementRef and directive instances', () => {
const Child = createDirective();
let childInstance, div;
/**
*
* class Cmpt {
* @ViewChildren('foo, bar') query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo', 'bar']));
div = E(1, 'div', [], 'foo');
{ childInstance = D(2, Child.ngDirectiveDef.n(), Child.ngDirectiveDef, 'bar'); }
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(2);
expect(query.first.nativeElement).toBe(div);
expect(query.last).toBe(childInstance);
});
it('should not add results to query if a requested token cant be read', () => {
const Child = createDirective();
let childInstance, div;
/**
*
* class Cmpt {
* @ViewChildren('foo', {read: Child}) query;
* }
*/
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
let tmp: any;
if (cm) {
m(0, Q(['foo'], false, Child));
div = E(1, 'div', [], 'foo');
e();
}
qR(tmp = m>(0)) && (ctx.query = tmp as QueryList);
});
const cmptInstance = renderComponent(Cmpt);
const query = (cmptInstance.query as QueryList);
expect(query.length).toBe(0);
});
});
});