Skip to content

Commit 4f05d02

Browse files
pkozlowski-opensourceIgorMinar
authored andcommitted
feat(core): support 'read' option for ngIvy queries (angular#20855)
PR Close angular#20855
1 parent 5df3431 commit 4f05d02

6 files changed

Lines changed: 263 additions & 24 deletions

File tree

packages/core/src/render3/di.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
import * as viewEngine from '../core';
1212

1313
import {LContainer, LElement, LNodeFlags, LNodeInjector} from './l_node';
14+
import {assertNodeType} from './node_assert';
1415
import {ComponentTemplate, DirectiveDef} from './public_interfaces';
1516
import {notImplemented, stringify} from './util';
1617

1718

19+
1820
/**
1921
* If a directive is diPublic, bloomAdd sets a property on the instance with this constant as
2022
* 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 {
316318
* @returns The TemplateRef instance to use
317319
*/
318320
export function getOrCreateTemplateRef<T>(di: LNodeInjector): viewEngine.TemplateRef<T> {
321+
ngDevMode && assertNodeType(di.node, LNodeFlags.Container);
319322
const data = (di.node as LContainer).data;
320-
if (data === null || data.template === null) {
321-
throw createInjectionError('Directive does not have a template.', null);
322-
}
323323
return di.templateRef ||
324324
(di.templateRef = new TemplateRef<any>(getOrCreateElementRef(di), data.template));
325325
}
@@ -328,7 +328,7 @@ export function getOrCreateTemplateRef<T>(di: LNodeInjector): viewEngine.Templat
328328
class TemplateRef<T> implements viewEngine.TemplateRef<T> {
329329
readonly elementRef: viewEngine.ElementRef;
330330

331-
constructor(elementRef: viewEngine.ElementRef, template: ComponentTemplate<T>) {
331+
constructor(elementRef: viewEngine.ElementRef, template: ComponentTemplate<T>|null) {
332332
this.elementRef = elementRef;
333333
}
334334

packages/core/src/render3/instructions.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import './ng_dev_mode';
1111
import {ElementRef, TemplateRef, Type, ViewContainerRef} from '../core';
1212

1313
import {assertEqual, assertLessThan, assertNotEqual, assertNotNull} from './assert';
14-
import {ContainerState, CssSelector, ProjectionState, QueryState, ViewState} from './interfaces';
14+
import {ContainerState, CssSelector, ProjectionState, QueryReadType, QueryState, ViewState} from './interfaces';
1515
import {LContainer, LElement, LNode, LNodeFlags, LNodeInjector, LProjection, LText, LView} from './l_node';
1616

1717
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
966966
* @param attrs The attrs attached to the container, if applicable
967967
*/
968968
export function containerStart(
969-
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[]): void {
969+
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[],
970+
localName?: string): void {
970971
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
971972

972973
// If the direct parent of the container is a view, its views (including its comment)
@@ -993,7 +994,7 @@ export function containerStart(
993994

994995
if (node.staticData == null) {
995996
node.staticData = ngStaticData[index] =
996-
createNodeStatic(tagName || null, attrs || null, [], null);
997+
createNodeStatic(tagName || null, attrs || null, [], localName || null);
997998
}
998999

9991000
// Containers are added to the current view tree instead of their embedded views
@@ -1009,6 +1010,8 @@ export function containerEnd() {
10091010
previousOrParentNode = previousOrParentNode.parent !;
10101011
}
10111012
ngDevMode && assertNodeType(previousOrParentNode, LNodeFlags.Container);
1013+
const query = previousOrParentNode.query;
1014+
query && query.addNode(previousOrParentNode);
10121015
}
10131016

10141017
/**
@@ -1716,11 +1719,12 @@ function valueInData<T>(data: any[], index: number, value?: T): T {
17161719
return value !;
17171720
}
17181721

1719-
export function query<T>(predicate: Type<any>| string[], descend?: boolean): QueryList<T> {
1722+
export function query<T>(
1723+
predicate: Type<any>| string[], descend?: boolean, read?: QueryReadType): QueryList<T> {
17201724
ngDevMode && assertPreviousIsParent();
17211725
const queryList = new QueryList<T>();
17221726
const query = currentQuery || (currentQuery = new QueryState_());
1723-
query.track(queryList, predicate, descend);
1727+
query.track(queryList, predicate, descend, read);
17241728
return queryList;
17251729
}
17261730

packages/core/src/render3/interfaces.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ export interface ViewOrContainerState {
210210
*/
211211
export type ProjectionState = Array<LElement|LText|LContainer>;
212212

213+
/**
214+
* An enum representing possible values of the "read" option for queries.
215+
*/
216+
export const enum QueryReadType {
217+
ElementRef = 0,
218+
ViewContainerRef = 1,
219+
TemplateRef = 2,
220+
}
213221

214222
/**
215223
* Used for tracking queries (e.g. ViewChild, ContentChild).
@@ -245,8 +253,11 @@ export interface QueryState {
245253
* @param queryList `QueryList` to update with changes.
246254
* @param predicate Either `Type` or selector array of [key, value] predicates.
247255
* @param descend If true the query will recursively apply to the children.
256+
* @param read Indicates which token should be read from DI for this query.
248257
*/
249-
track<T>(queryList: QueryList<T>, predicate: Type<T>|any[], descend?: boolean): void;
258+
track<T>(
259+
queryList: QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
260+
read?: QueryReadType): void;
250261
}
251262

252263
/**

packages/core/src/render3/node_assert.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ export function assertNodeType(node: LNode, type: LNodeFlags) {
1414
assertEqual(node.flags & LNodeFlags.TYPE_MASK, type, 'Node.type', typeSerializer);
1515
}
1616

17+
export function assertNodeOfPossibleTypes(node: LNode, ...types: LNodeFlags[]) {
18+
assertNotEqual(node, null, 'node');
19+
const nodeType = (node.flags & LNodeFlags.TYPE_MASK);
20+
for (let i = 0; i < types.length; i++) {
21+
if (nodeType === types[i]) {
22+
return;
23+
}
24+
}
25+
throw new Error(
26+
`Expected node of possible types: ${types.map(typeSerializer).join(', ')} but got ${typeSerializer(nodeType)}`);
27+
}
1728

1829
function typeSerializer(type: LNodeFlags): string {
1930
if (type == LNodeFlags.Projection) return 'Projection';

packages/core/src/render3/query.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import {Observable} from 'rxjs/Observable';
1313
import * as viewEngine from '../core';
1414

1515
import {assertNotNull} from './assert';
16-
import {getOrCreateElementRef, getOrCreateNodeInjectorForNode} from './di';
17-
import {QueryState} from './interfaces';
18-
import {LContainer, LElement, LNode, LNodeFlags, LView} from './l_node';
16+
import {getOrCreateContainerRef, getOrCreateElementRef, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from './di';
17+
import {QueryReadType, QueryState} from './interfaces';
18+
import {LContainer, LElement, LNode, LNodeFlags, LNodeInjector, LView} from './l_node';
19+
import {assertNodeOfPossibleTypes} from './node_assert';
1920
import {DirectiveDef} from './public_interfaces';
2021

2122

@@ -44,6 +45,11 @@ export interface QueryPredicate<T> {
4445
*/
4546
selector: string[]|null;
4647

48+
/**
49+
* Indicates which token should be read from DI for this query.
50+
*/
51+
read: QueryReadType|null;
52+
4753
/**
4854
* Values which have been located.
4955
*
@@ -59,14 +65,15 @@ export class QueryState_ implements QueryState {
5965
constructor(deep?: QueryPredicate<any>) { this.deep = deep == null ? null : deep; }
6066

6167
track<T>(
62-
queryList: viewEngine.QueryList<T>, predicate: viewEngine.Type<T>|string[],
63-
descend?: boolean): void {
68+
queryList: viewEngine.QueryList<T>, predicate: viewEngine.Type<T>|string[], descend?: boolean,
69+
read?: QueryReadType): void {
6470
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
6571
// mutate parent.
6672
if (descend) {
67-
this.deep = createPredicate(this.deep, queryList, predicate);
73+
this.deep = createPredicate(this.deep, queryList, predicate, read != null ? read : null);
6874
} else {
69-
this.shallow = createPredicate(this.shallow, queryList, predicate);
75+
this.shallow =
76+
createPredicate(this.shallow, queryList, predicate, read != null ? read : null);
7077
}
7178
}
7279

@@ -99,6 +106,33 @@ export class QueryState_ implements QueryState {
99106
}
100107
}
101108

109+
function readDefaultInjectable(nodeInjector: LNodeInjector, node: LNode):
110+
viewEngine.ElementRef|viewEngine.TemplateRef<any>|undefined {
111+
ngDevMode && assertNodeOfPossibleTypes(node, LNodeFlags.Container, LNodeFlags.Element);
112+
if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {
113+
return getOrCreateElementRef(nodeInjector);
114+
} else if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Container) {
115+
return getOrCreateTemplateRef(nodeInjector);
116+
}
117+
}
118+
119+
function readFromNodeInjector(nodeInjector: LNodeInjector, node: LNode, read: QueryReadType | null):
120+
viewEngine.ElementRef|viewEngine.ViewContainerRef|viewEngine.TemplateRef<any>|undefined {
121+
if (read === null) {
122+
return readDefaultInjectable(nodeInjector, node);
123+
} else if (read === QueryReadType.ElementRef) {
124+
return getOrCreateElementRef(nodeInjector);
125+
} else if (read === QueryReadType.ViewContainerRef) {
126+
return getOrCreateContainerRef(nodeInjector);
127+
} else if (read === QueryReadType.TemplateRef) {
128+
return getOrCreateTemplateRef(nodeInjector);
129+
}
130+
131+
if (ngDevMode) {
132+
throw new Error(`Unrecognised read type for queries: ${read}`);
133+
}
134+
}
135+
102136
function add(predicate: QueryPredicate<any>| null, node: LNode) {
103137
while (predicate) {
104138
const type = predicate.type;
@@ -115,12 +149,14 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
115149
}
116150
} else {
117151
const staticData = node.staticData;
152+
const nodeInjector = getOrCreateNodeInjectorForNode(node as LElement | LContainer);
118153
if (staticData && staticData.localName) {
119154
const selector = predicate.selector !;
120155
for (let i = 0; i < selector.length; i++) {
121156
if (selector[i] === staticData.localName) {
122-
predicate.values.push(getOrCreateElementRef(
123-
getOrCreateNodeInjectorForNode(node as LElement | LContainer)));
157+
const injectable = readFromNodeInjector(nodeInjector, node, predicate.read);
158+
assertNotNull(injectable, 'injectable');
159+
predicate.values.push(injectable);
124160
}
125161
}
126162
}
@@ -131,7 +167,7 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
131167

132168
function createPredicate<T>(
133169
previous: QueryPredicate<any>| null, queryList: QueryList<T>,
134-
predicate: viewEngine.Type<T>| string[]): QueryPredicate<T> {
170+
predicate: viewEngine.Type<T>| string[], read: QueryReadType | null): QueryPredicate<T> {
135171
const isArray = Array.isArray(predicate);
136172
const values = <any>[];
137173
if ((queryList as any as QueryList_<T>)._valuesTree === null) {
@@ -142,6 +178,7 @@ function createPredicate<T>(
142178
list: queryList,
143179
type: isArray ? null : predicate as viewEngine.Type<T>,
144180
selector: isArray ? predicate as string[] : null,
181+
read: read,
145182
values: values
146183
};
147184
}

0 commit comments

Comments
 (0)