feat(core): support 'read' option for ngIvy queries (#20855)
PR Close #20855
This commit is contained in:
parent
5df343169e
commit
4f05d022c1
|
@ -11,10 +11,12 @@
|
||||||
import * as viewEngine from '../core';
|
import * as viewEngine from '../core';
|
||||||
|
|
||||||
import {LContainer, LElement, LNodeFlags, LNodeInjector} from './l_node';
|
import {LContainer, LElement, LNodeFlags, LNodeInjector} from './l_node';
|
||||||
|
import {assertNodeType} from './node_assert';
|
||||||
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
|
||||||
import {notImplemented, stringify} from './util';
|
import {notImplemented, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a directive is diPublic, bloomAdd sets a property on the instance with this constant as
|
* If a directive is diPublic, bloomAdd sets a property on the instance with this constant as
|
||||||
* the key and the directive's unique ID as the value. This allows us to map directives to their
|
* the key and the directive's unique ID as the value. This allows us to map directives to their
|
||||||
|
@ -316,10 +318,8 @@ class ElementRef implements viewEngine.ElementRef {
|
||||||
* @returns The TemplateRef instance to use
|
* @returns The TemplateRef instance to use
|
||||||
*/
|
*/
|
||||||
export function getOrCreateTemplateRef<T>(di: LNodeInjector): viewEngine.TemplateRef<T> {
|
export function getOrCreateTemplateRef<T>(di: LNodeInjector): viewEngine.TemplateRef<T> {
|
||||||
|
ngDevMode && assertNodeType(di.node, LNodeFlags.Container);
|
||||||
const data = (di.node as LContainer).data;
|
const data = (di.node as LContainer).data;
|
||||||
if (data === null || data.template === null) {
|
|
||||||
throw createInjectionError('Directive does not have a template.', null);
|
|
||||||
}
|
|
||||||
return di.templateRef ||
|
return di.templateRef ||
|
||||||
(di.templateRef = new TemplateRef<any>(getOrCreateElementRef(di), data.template));
|
(di.templateRef = new TemplateRef<any>(getOrCreateElementRef(di), data.template));
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,7 @@ export function getOrCreateTemplateRef<T>(di: LNodeInjector): viewEngine.Templat
|
||||||
class TemplateRef<T> implements viewEngine.TemplateRef<T> {
|
class TemplateRef<T> implements viewEngine.TemplateRef<T> {
|
||||||
readonly elementRef: viewEngine.ElementRef;
|
readonly elementRef: viewEngine.ElementRef;
|
||||||
|
|
||||||
constructor(elementRef: viewEngine.ElementRef, template: ComponentTemplate<T>) {
|
constructor(elementRef: viewEngine.ElementRef, template: ComponentTemplate<T>|null) {
|
||||||
this.elementRef = elementRef;
|
this.elementRef = elementRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import './ng_dev_mode';
|
||||||
import {ElementRef, TemplateRef, Type, ViewContainerRef} from '../core';
|
import {ElementRef, TemplateRef, Type, ViewContainerRef} from '../core';
|
||||||
|
|
||||||
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
|
||||||
import {ContainerState, CssSelector, ProjectionState, QueryState, ViewState} from './interfaces';
|
import {ContainerState, CssSelector, ProjectionState, QueryReadType, QueryState, ViewState} from './interfaces';
|
||||||
import {LContainer, LElement, LNode, LNodeFlags, LNodeInjector, LProjection, LText, LView} from './l_node';
|
import {LContainer, LElement, LNode, LNodeFlags, LNodeInjector, LProjection, LText, LView} from './l_node';
|
||||||
|
|
||||||
import {NgStaticData, LNodeStatic, LContainerStatic, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './l_node_static';
|
import {NgStaticData, LNodeStatic, LContainerStatic, InitialInputData, InitialInputs, PropertyAliases, PropertyAliasValue,} from './l_node_static';
|
||||||
|
@ -966,7 +966,8 @@ export function lifecycle(lifeCycle: LifecycleHook, self?: any, method?: Functio
|
||||||
* @param attrs The attrs attached to the container, if applicable
|
* @param attrs The attrs attached to the container, if applicable
|
||||||
*/
|
*/
|
||||||
export function containerStart(
|
export function containerStart(
|
||||||
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[]): void {
|
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[],
|
||||||
|
localName?: string): void {
|
||||||
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
|
||||||
|
|
||||||
// If the direct parent of the container is a view, its views (including its comment)
|
// If the direct parent of the container is a view, its views (including its comment)
|
||||||
|
@ -993,7 +994,7 @@ export function containerStart(
|
||||||
|
|
||||||
if (node.staticData == null) {
|
if (node.staticData == null) {
|
||||||
node.staticData = ngStaticData[index] =
|
node.staticData = ngStaticData[index] =
|
||||||
createNodeStatic(tagName || null, attrs || null, [], null);
|
createNodeStatic(tagName || null, attrs || null, [], localName || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Containers are added to the current view tree instead of their embedded views
|
// Containers are added to the current view tree instead of their embedded views
|
||||||
|
@ -1009,6 +1010,8 @@ export function containerEnd() {
|
||||||
previousOrParentNode = previousOrParentNode.parent !;
|
previousOrParentNode = previousOrParentNode.parent !;
|
||||||
}
|
}
|
||||||
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
|
||||||
|
const query = previousOrParentNode.query;
|
||||||
|
query && query.addNode(previousOrParentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1716,11 +1719,12 @@ function valueInData<T>(data: any[], index: number, value?: T): T {
|
||||||
return value !;
|
return value !;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function query<T>(predicate: Type<any>| string[], descend?: boolean): QueryList<T> {
|
export function query<T>(
|
||||||
|
predicate: Type<any>| string[], descend?: boolean, read?: QueryReadType): QueryList<T> {
|
||||||
ngDevMode && assertPreviousIsParent();
|
ngDevMode && assertPreviousIsParent();
|
||||||
const queryList = new QueryList<T>();
|
const queryList = new QueryList<T>();
|
||||||
const query = currentQuery || (currentQuery = new QueryState_());
|
const query = currentQuery || (currentQuery = new QueryState_());
|
||||||
query.track(queryList, predicate, descend);
|
query.track(queryList, predicate, descend, read);
|
||||||
return queryList;
|
return queryList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,14 @@ export interface ViewOrContainerState {
|
||||||
*/
|
*/
|
||||||
export type ProjectionState = Array<LElement|LText|LContainer>;
|
export type ProjectionState = Array<LElement|LText|LContainer>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enum representing possible values of the "read" option for queries.
|
||||||
|
*/
|
||||||
|
export const enum QueryReadType {
|
||||||
|
ElementRef = 0,
|
||||||
|
ViewContainerRef = 1,
|
||||||
|
TemplateRef = 2,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for tracking queries (e.g. ViewChild, ContentChild).
|
* Used for tracking queries (e.g. ViewChild, ContentChild).
|
||||||
|
@ -245,8 +253,11 @@ export interface QueryState {
|
||||||
* @param queryList `QueryList` to update with changes.
|
* @param queryList `QueryList` to update with changes.
|
||||||
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
||||||
* @param descend If true the query will recursively apply to the children.
|
* @param descend If true the query will recursively apply to the children.
|
||||||
|
* @param read Indicates which token should be read from DI for this query.
|
||||||
*/
|
*/
|
||||||
track<T>(queryList: QueryList<T>, predicate: Type<T>|any[], descend?: boolean): void;
|
track<T>(
|
||||||
|
queryList: QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
||||||
|
read?: QueryReadType): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,6 +14,17 @@ export function assertNodeType(node: LNode, type: LNodeFlags) {
|
||||||
assertEqual(node.flags & LNodeFlags.TYPE_MASK, type, 'Node.type', typeSerializer);
|
assertEqual(node.flags & LNodeFlags.TYPE_MASK, type, 'Node.type', typeSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertNodeOfPossibleTypes(node: LNode, ...types: LNodeFlags[]) {
|
||||||
|
assertNotEqual(node, null, 'node');
|
||||||
|
const nodeType = (node.flags & LNodeFlags.TYPE_MASK);
|
||||||
|
for (let i = 0; i < types.length; i++) {
|
||||||
|
if (nodeType === types[i]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Expected node of possible types: ${types.map(typeSerializer).join(', ')} but got ${typeSerializer(nodeType)}`);
|
||||||
|
}
|
||||||
|
|
||||||
function typeSerializer(type: LNodeFlags): string {
|
function typeSerializer(type: LNodeFlags): string {
|
||||||
if (type == LNodeFlags.Projection) return 'Projection';
|
if (type == LNodeFlags.Projection) return 'Projection';
|
||||||
|
|
|
@ -13,9 +13,10 @@ import {Observable} from 'rxjs/Observable';
|
||||||
import * as viewEngine from '../core';
|
import * as viewEngine from '../core';
|
||||||
|
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {getOrCreateElementRef, getOrCreateNodeInjectorForNode} from './di';
|
import {getOrCreateContainerRef, getOrCreateElementRef, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from './di';
|
||||||
import {QueryState} from './interfaces';
|
import {QueryReadType, QueryState} from './interfaces';
|
||||||
import {LContainer, LElement, LNode, LNodeFlags, LView} from './l_node';
|
import {LContainer, LElement, LNode, LNodeFlags, LNodeInjector, LView} from './l_node';
|
||||||
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {DirectiveDef} from './public_interfaces';
|
import {DirectiveDef} from './public_interfaces';
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,6 +45,11 @@ export interface QueryPredicate<T> {
|
||||||
*/
|
*/
|
||||||
selector: string[]|null;
|
selector: string[]|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates which token should be read from DI for this query.
|
||||||
|
*/
|
||||||
|
read: QueryReadType|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Values which have been located.
|
* Values which have been located.
|
||||||
*
|
*
|
||||||
|
@ -59,14 +65,15 @@ export class QueryState_ implements QueryState {
|
||||||
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
|
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
|
||||||
|
|
||||||
track<T>(
|
track<T>(
|
||||||
queryList: viewEngine.QueryList<T>, predicate: viewEngine.Type<T>|string[],
|
queryList: viewEngine.QueryList<T>, predicate: viewEngine.Type<T>|string[], descend?: boolean,
|
||||||
descend?: boolean): void {
|
read?: QueryReadType): void {
|
||||||
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
|
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
|
||||||
// mutate parent.
|
// mutate parent.
|
||||||
if (descend) {
|
if (descend) {
|
||||||
this.deep = createPredicate(this.deep, queryList, predicate);
|
this.deep = createPredicate(this.deep, queryList, predicate, read != null ? read : null);
|
||||||
} else {
|
} else {
|
||||||
this.shallow = createPredicate(this.shallow, queryList, predicate);
|
this.shallow =
|
||||||
|
createPredicate(this.shallow, queryList, predicate, read != null ? read : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +106,33 @@ export class QueryState_ implements QueryState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readDefaultInjectable(nodeInjector: LNodeInjector, node: LNode):
|
||||||
|
viewEngine.ElementRef|viewEngine.TemplateRef<any>|undefined {
|
||||||
|
ngDevMode && assertNodeOfPossibleTypes(node, LNodeFlags.Container, LNodeFlags.Element);
|
||||||
|
if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {
|
||||||
|
return getOrCreateElementRef(nodeInjector);
|
||||||
|
} else if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Container) {
|
||||||
|
return getOrCreateTemplateRef(nodeInjector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readFromNodeInjector(nodeInjector: LNodeInjector, node: LNode, read: QueryReadType | null):
|
||||||
|
viewEngine.ElementRef|viewEngine.ViewContainerRef|viewEngine.TemplateRef<any>|undefined {
|
||||||
|
if (read === null) {
|
||||||
|
return readDefaultInjectable(nodeInjector, node);
|
||||||
|
} else if (read === QueryReadType.ElementRef) {
|
||||||
|
return getOrCreateElementRef(nodeInjector);
|
||||||
|
} else if (read === QueryReadType.ViewContainerRef) {
|
||||||
|
return getOrCreateContainerRef(nodeInjector);
|
||||||
|
} else if (read === QueryReadType.TemplateRef) {
|
||||||
|
return getOrCreateTemplateRef(nodeInjector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ngDevMode) {
|
||||||
|
throw new Error(`Unrecognised read type for queries: ${read}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||||
while (predicate) {
|
while (predicate) {
|
||||||
const type = predicate.type;
|
const type = predicate.type;
|
||||||
|
@ -115,12 +149,14 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const staticData = node.staticData;
|
const staticData = node.staticData;
|
||||||
|
const nodeInjector = getOrCreateNodeInjectorForNode(node as LElement | LContainer);
|
||||||
if (staticData && staticData.localName) {
|
if (staticData && staticData.localName) {
|
||||||
const selector = predicate.selector !;
|
const selector = predicate.selector !;
|
||||||
for (let i = 0; i < selector.length; i++) {
|
for (let i = 0; i < selector.length; i++) {
|
||||||
if (selector[i] === staticData.localName) {
|
if (selector[i] === staticData.localName) {
|
||||||
predicate.values.push(getOrCreateElementRef(
|
const injectable = readFromNodeInjector(nodeInjector, node, predicate.read);
|
||||||
getOrCreateNodeInjectorForNode(node as LElement | LContainer)));
|
assertNotNull(injectable, 'injectable');
|
||||||
|
predicate.values.push(injectable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +167,7 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||||
|
|
||||||
function createPredicate<T>(
|
function createPredicate<T>(
|
||||||
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
|
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
|
||||||
predicate: viewEngine.Type<T>| string[]): QueryPredicate<T> {
|
predicate: viewEngine.Type<T>| string[], read: QueryReadType | null): QueryPredicate<T> {
|
||||||
const isArray = Array.isArray(predicate);
|
const isArray = Array.isArray(predicate);
|
||||||
const values = <any>[];
|
const values = <any>[];
|
||||||
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
|
||||||
|
@ -142,6 +178,7 @@ function createPredicate<T>(
|
||||||
list: queryList,
|
list: queryList,
|
||||||
type: isArray ? null : predicate as viewEngine.Type<T>,
|
type: isArray ? null : predicate as viewEngine.Type<T>,
|
||||||
selector: isArray ? predicate as string[] : null,
|
selector: isArray ? predicate as string[] : null,
|
||||||
|
read: read,
|
||||||
values: values
|
values: values
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,39 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* 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 {D, E, Q, QueryList, e, m, qR} from '../../src/render3/index';
|
import {QueryReadType} from '../../src/render3/interfaces';
|
||||||
|
|
||||||
import {createComponent, renderComponent} from './render_util';
|
import {createComponent, renderComponent} from './render_util';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to check if a given candidate object resembles ElementRef
|
||||||
|
* @param candidate
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isElementRef(candidate: any): boolean {
|
||||||
|
return candidate.nativeElement != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to check if a given candidate object resembles TemplateRef
|
||||||
|
* @param candidate
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
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 {boolean}
|
||||||
|
*/
|
||||||
|
function isViewContainerRef(candidate: any): boolean {
|
||||||
|
return candidate.createEmbeddedView != null && candidate.createComponent != null;
|
||||||
|
}
|
||||||
|
|
||||||
describe('query', () => {
|
describe('query', () => {
|
||||||
it('should project query children', () => {
|
it('should project query children', () => {
|
||||||
const Child = createComponent('child', function(ctx: any, cm: boolean) {});
|
const Child = createComponent('child', function(ctx: any, cm: boolean) {});
|
||||||
|
@ -51,7 +79,7 @@ describe('query', () => {
|
||||||
|
|
||||||
describe('local names', () => {
|
describe('local names', () => {
|
||||||
|
|
||||||
it('should query for a single element', () => {
|
it('should query for a single element and read ElementRef', () => {
|
||||||
|
|
||||||
let elToQuery;
|
let elToQuery;
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +107,7 @@ describe('query', () => {
|
||||||
expect(query.first.nativeElement).toEqual(elToQuery);
|
expect(query.first.nativeElement).toEqual(elToQuery);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should query for multiple elements', () => {
|
it('should query for multiple elements and read ElementRef', () => {
|
||||||
|
|
||||||
let el1ToQuery;
|
let el1ToQuery;
|
||||||
let el2ToQuery;
|
let el2ToQuery;
|
||||||
|
@ -112,5 +140,153 @@ describe('query', () => {
|
||||||
expect(query.last.nativeElement).toEqual(el2ToQuery);
|
expect(query.last.nativeElement).toEqual(el2ToQuery);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should read ElementRef from an element when explicitly asked for', () => {
|
||||||
|
|
||||||
|
let elToQuery;
|
||||||
|
/**
|
||||||
|
* <div #foo></div>
|
||||||
|
* <div></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'], false, QueryReadType.ElementRef));
|
||||||
|
elToQuery = E(1, 'div', [], 'foo');
|
||||||
|
e();
|
||||||
|
E(2, 'div');
|
||||||
|
e();
|
||||||
|
}
|
||||||
|
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
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', () => {
|
||||||
|
/**
|
||||||
|
* <div #foo></div>
|
||||||
|
* 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<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(isViewContainerRef(query.first)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should read ViewContainerRef from container nodes when explicitly asked for', () => {
|
||||||
|
/**
|
||||||
|
* <ng-template #foo></ng-template>
|
||||||
|
* 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<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
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',
|
||||||
|
() => {
|
||||||
|
/**
|
||||||
|
* <ng-template #foo></ng-template>
|
||||||
|
* 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<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
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
|
||||||
|
/**
|
||||||
|
* <ng-template #foo></ng-template>
|
||||||
|
* 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<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(isTemplateRef(query.first)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should read TemplateRef from container nodes when explicitly asked for', () => {
|
||||||
|
/**
|
||||||
|
* <ng-template #foo></ng-template>
|
||||||
|
* 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<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
});
|
||||||
|
|
||||||
|
const cmptInstance = renderComponent(Cmpt);
|
||||||
|
const query = (cmptInstance.query as QueryList<any>);
|
||||||
|
expect(query.length).toBe(1);
|
||||||
|
expect(isTemplateRef(query.first)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue