refactor(ivy): avoid circular dep with query/di/instructions (#21430)
To prepare for pending ngForOf work, the dep from instructions -> query should be broken. This will enable a dep from di -> instructions while avoiding a di -> instructions -> query -> di cycle. Analyzing this cycle also uncovered another problem: the implementation of query() breaks tree-shaking through a hard dependency on DI concepts of TemplateRef, ElementRef, ViewContainerRef. This is fundamentally due to how query() can query for those values without any configuration. Instead, this fix introduces the concept by employing the strategy pattern, and redefining QueryReadType to pass a function which will return one of the above values. This strategy is then used for 'read' instead of an enum in cases where special values should be read from the DI system. PR Close #21430
This commit is contained in:
parent
c5586b7dfa
commit
6472661ae8
|
@ -10,7 +10,8 @@ import {RendererType2} from '../render/api';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
import {resolveRendererType2} from '../view/util';
|
import {resolveRendererType2} from '../view/util';
|
||||||
|
|
||||||
import {componentRefresh, diPublic} from './instructions';
|
import {diPublic} from './di';
|
||||||
|
import {componentRefresh} from './instructions';
|
||||||
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs} from './interfaces/definition';
|
import {ComponentDef, ComponentDefArgs, DirectiveDef, DirectiveDefArgs} from './interfaces/definition';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,12 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
|
||||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
|
import {assertPreviousIsParent, getPreviousOrParentNode} from './instructions';
|
||||||
import {ComponentTemplate, DirectiveDef, TypedDirectiveDef} from './interfaces/definition';
|
import {ComponentTemplate, DirectiveDef, TypedDirectiveDef} from './interfaces/definition';
|
||||||
import {LInjector} from './interfaces/injector';
|
import {LInjector} from './interfaces/injector';
|
||||||
import {LContainerNode, LElementNode, LNodeFlags} from './interfaces/node';
|
import {LContainerNode, LElementNode, LNode, LNodeFlags} from './interfaces/node';
|
||||||
import {assertNodeType} from './node_assert';
|
import {QueryReadType} from './interfaces/query';
|
||||||
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {notImplemented, stringify} from './util';
|
import {notImplemented, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,6 +89,11 @@ export function bloomAdd(injector: LInjector, type: Type<any>): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOrCreateNodeInjector(): LInjector {
|
||||||
|
ngDevMode && assertPreviousIsParent();
|
||||||
|
return getOrCreateNodeInjectorForNode(getPreviousOrParentNode() as LElementNode | LContainerNode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates (or gets an existing) injector for a given element or container.
|
* Creates (or gets an existing) injector for a given element or container.
|
||||||
*
|
*
|
||||||
|
@ -151,6 +158,71 @@ export function diPublicInInjector(di: LInjector, def: TypedDirectiveDef<any>):
|
||||||
bloomAdd(di, def.type);
|
bloomAdd(di, def.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
|
||||||
|
*
|
||||||
|
* @param def The definition of the directive to be made public
|
||||||
|
*/
|
||||||
|
export function diPublic(def: TypedDirectiveDef<any>): void {
|
||||||
|
diPublicInInjector(getOrCreateNodeInjector(), def);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for an instance of the given directive type up the injector tree and returns
|
||||||
|
* that instance if found.
|
||||||
|
*
|
||||||
|
* If not found, it will propagate up to the next parent injector until the token
|
||||||
|
* is found or the top is reached.
|
||||||
|
*
|
||||||
|
* Usage example (in factory function):
|
||||||
|
*
|
||||||
|
* class SomeDirective {
|
||||||
|
* constructor(directive: DirectiveA) {}
|
||||||
|
*
|
||||||
|
* static ngDirectiveDef = defineDirective({
|
||||||
|
* type: SomeDirective,
|
||||||
|
* factory: () => new SomeDirective(inject(DirectiveA))
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param token The directive type to search for
|
||||||
|
* @param flags Injection flags (e.g. CheckParent)
|
||||||
|
* @returns The instance found
|
||||||
|
*/
|
||||||
|
export function inject<T>(token: Type<T>, flags?: InjectFlags): T {
|
||||||
|
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an ElementRef and stores it on the injector.
|
||||||
|
* Or, if the ElementRef already exists, retrieves the existing ElementRef.
|
||||||
|
*
|
||||||
|
* @returns The ElementRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectElementRef(): viewEngine_ElementRef {
|
||||||
|
return getOrCreateElementRef(getOrCreateNodeInjector());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TemplateRef and stores it on the injector. Or, if the TemplateRef already
|
||||||
|
* exists, retrieves the existing TemplateRef.
|
||||||
|
*
|
||||||
|
* @returns The TemplateRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectTemplateRef<T>(): viewEngine_TemplateRef<T> {
|
||||||
|
return getOrCreateTemplateRef<T>(getOrCreateNodeInjector());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
|
||||||
|
* already exists, retrieves the existing ViewContainerRef.
|
||||||
|
*
|
||||||
|
* @returns The ViewContainerRef instance to use
|
||||||
|
*/
|
||||||
|
export function injectViewContainerRef(): viewEngine_ViewContainerRef {
|
||||||
|
return getOrCreateContainerRef(getOrCreateNodeInjector());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for an instance of the given directive type up the injector tree and returns
|
* Searches for an instance of the given directive type up the injector tree and returns
|
||||||
* that instance if found.
|
* that instance if found.
|
||||||
|
@ -300,6 +372,10 @@ export function bloomFindPossibleInjector(startInjector: LInjector, bloomBit: nu
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ReadFromInjectorFn<T> {
|
||||||
|
constructor(readonly read: (injector: LInjector, node: LNode, directiveIndex?: number) => T) {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an ElementRef for a given node injector and stores it on the injector.
|
* Creates an ElementRef for a given node injector and stores it on the injector.
|
||||||
* Or, if the ElementRef already exists, retrieves the existing ElementRef.
|
* Or, if the ElementRef already exists, retrieves the existing ElementRef.
|
||||||
|
@ -311,6 +387,31 @@ export function getOrCreateElementRef(di: LInjector): viewEngine_ElementRef {
|
||||||
return di.elementRef || (di.elementRef = new ElementRef(di.node.native));
|
return di.elementRef || (di.elementRef = new ElementRef(di.node.native));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const QUERY_READ_TEMPLATE_REF = <QueryReadType<viewEngine_TemplateRef<any>>>(
|
||||||
|
new ReadFromInjectorFn<viewEngine_TemplateRef<any>>(
|
||||||
|
(injector: LInjector) => getOrCreateTemplateRef(injector)) as any);
|
||||||
|
|
||||||
|
export const QUERY_READ_CONTAINER_REF = <QueryReadType<viewEngine_ViewContainerRef>>(
|
||||||
|
new ReadFromInjectorFn<viewEngine_ViewContainerRef>(
|
||||||
|
(injector: LInjector) => getOrCreateContainerRef(injector)) as any);
|
||||||
|
|
||||||
|
export const QUERY_READ_ELEMENT_REF =
|
||||||
|
<QueryReadType<viewEngine_ElementRef>>(new ReadFromInjectorFn<viewEngine_ElementRef>(
|
||||||
|
(injector: LInjector) => getOrCreateElementRef(injector)) as any);
|
||||||
|
|
||||||
|
export const QUERY_READ_FROM_NODE =
|
||||||
|
(new ReadFromInjectorFn<any>((injector: LInjector, node: LNode, directiveIdx: number) => {
|
||||||
|
ngDevMode && assertNodeOfPossibleTypes(node, LNodeFlags.Container, LNodeFlags.Element);
|
||||||
|
if (directiveIdx > -1) {
|
||||||
|
return node.view.data[directiveIdx];
|
||||||
|
} else if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Element) {
|
||||||
|
return getOrCreateElementRef(injector);
|
||||||
|
} else if ((node.flags & LNodeFlags.TYPE_MASK) === LNodeFlags.Container) {
|
||||||
|
return getOrCreateTemplateRef(injector);
|
||||||
|
}
|
||||||
|
throw new Error('fail');
|
||||||
|
}) as any as QueryReadType<any>);
|
||||||
|
|
||||||
/** A ref to a node's native element. */
|
/** A ref to a node's native element. */
|
||||||
class ElementRef implements viewEngine_ElementRef {
|
class ElementRef implements viewEngine_ElementRef {
|
||||||
readonly nativeElement: any;
|
readonly nativeElement: any;
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
|
|
||||||
import {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent} from './component';
|
import {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent} from './component';
|
||||||
import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective} from './definition';
|
import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective} from './definition';
|
||||||
|
import {InjectFlags} from './di';
|
||||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';
|
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';
|
||||||
|
|
||||||
|
export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
|
||||||
|
|
||||||
// Naming scheme:
|
// Naming scheme:
|
||||||
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
|
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
|
||||||
// C(Container), L(Listener)
|
// C(Container), L(Listener)
|
||||||
|
@ -20,7 +23,6 @@ import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveD
|
||||||
// - lower case for closing: c(containerEnd), e(elementEnd), v(viewEnd)
|
// - lower case for closing: c(containerEnd), e(elementEnd), v(viewEnd)
|
||||||
// clang-format off
|
// clang-format off
|
||||||
export {
|
export {
|
||||||
inject, injectElementRef, injectTemplateRef, injectViewContainerRef,
|
|
||||||
|
|
||||||
LifecycleHook,
|
LifecycleHook,
|
||||||
|
|
||||||
|
@ -57,17 +59,21 @@ export {
|
||||||
projection as P,
|
projection as P,
|
||||||
projectionDef as pD,
|
projectionDef as pD,
|
||||||
|
|
||||||
query as Q,
|
|
||||||
queryRefresh as qR,
|
|
||||||
|
|
||||||
text as T,
|
text as T,
|
||||||
textBinding as t,
|
textBinding as t,
|
||||||
|
|
||||||
viewStart as V,
|
viewStart as V,
|
||||||
viewEnd as v,
|
viewEnd as v,
|
||||||
} from './instructions';
|
} from './instructions';
|
||||||
|
|
||||||
|
export {
|
||||||
|
QueryList,
|
||||||
|
|
||||||
|
query as Q,
|
||||||
|
queryRefresh as qR,
|
||||||
|
} from './query';
|
||||||
// clang-format on
|
// clang-format on
|
||||||
export {QueryList} from './query';
|
|
||||||
export {
|
export {
|
||||||
ComponentDef,
|
ComponentDef,
|
||||||
ComponentTemplate,
|
ComponentTemplate,
|
||||||
|
@ -81,4 +87,3 @@ export {
|
||||||
defineDirective,
|
defineDirective,
|
||||||
};
|
};
|
||||||
export {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent};
|
export {createComponentRef, detectChanges, getHostElement, markDirty, renderComponent};
|
||||||
export {InjectFlags} from './di';
|
|
||||||
|
|
|
@ -25,12 +25,9 @@ import {assertNodeType} from './node_assert';
|
||||||
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
import {appendChild, insertChild, insertView, processProjectedNode, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelector} from './node_selector_matcher';
|
import {isNodeMatchingSelector} from './node_selector_matcher';
|
||||||
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, TypedDirectiveDef, TypedComponentDef} from './interfaces/definition';
|
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, TypedDirectiveDef, TypedComponentDef} from './interfaces/definition';
|
||||||
import {InjectFlags, diPublicInInjector, getOrCreateNodeInjectorForNode, getOrCreateElementRef, getOrCreateTemplateRef, getOrCreateContainerRef, getOrCreateInjectable} from './di';
|
|
||||||
import {QueryList, LQuery_} from './query';
|
|
||||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './interfaces/renderer';
|
import {RComment, RElement, RText, Renderer3, RendererFactory3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './interfaces/renderer';
|
||||||
import {isDifferent, stringify} from './util';
|
import {isDifferent, stringify} from './util';
|
||||||
|
|
||||||
export {queryRefresh} from './query';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum used by the lifecycle (l) instruction to determine which lifecycle hook is requesting
|
* Enum used by the lifecycle (l) instruction to determine which lifecycle hook is requesting
|
||||||
|
@ -331,76 +328,6 @@ export function renderComponentOrTemplate<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrCreateNodeInjector(): LInjector {
|
|
||||||
ngDevMode && assertPreviousIsParent();
|
|
||||||
return getOrCreateNodeInjectorForNode(previousOrParentNode as LElementNode | LContainerNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
|
|
||||||
*
|
|
||||||
* @param def The definition of the directive to be made public
|
|
||||||
*/
|
|
||||||
export function diPublic(def: TypedDirectiveDef<any>): void {
|
|
||||||
diPublicInInjector(getOrCreateNodeInjector(), def);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for an instance of the given directive type up the injector tree and returns
|
|
||||||
* that instance if found.
|
|
||||||
*
|
|
||||||
* If not found, it will propagate up to the next parent injector until the token
|
|
||||||
* is found or the top is reached.
|
|
||||||
*
|
|
||||||
* Usage example (in factory function):
|
|
||||||
*
|
|
||||||
* class SomeDirective {
|
|
||||||
* constructor(directive: DirectiveA) {}
|
|
||||||
*
|
|
||||||
* static ngDirectiveDef = defineDirective({
|
|
||||||
* type: SomeDirective,
|
|
||||||
* factory: () => new SomeDirective(inject(DirectiveA))
|
|
||||||
* });
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @param token The directive type to search for
|
|
||||||
* @param flags Injection flags (e.g. CheckParent)
|
|
||||||
* @returns The instance found
|
|
||||||
*/
|
|
||||||
export function inject<T>(token: Type<T>, flags?: InjectFlags): T {
|
|
||||||
return getOrCreateInjectable<T>(getOrCreateNodeInjector(), token, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an ElementRef and stores it on the injector.
|
|
||||||
* Or, if the ElementRef already exists, retrieves the existing ElementRef.
|
|
||||||
*
|
|
||||||
* @returns The ElementRef instance to use
|
|
||||||
*/
|
|
||||||
export function injectElementRef(): ElementRef {
|
|
||||||
return getOrCreateElementRef(getOrCreateNodeInjector());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a TemplateRef and stores it on the injector. Or, if the TemplateRef already
|
|
||||||
* exists, retrieves the existing TemplateRef.
|
|
||||||
*
|
|
||||||
* @returns The TemplateRef instance to use
|
|
||||||
*/
|
|
||||||
export function injectTemplateRef<T>(): TemplateRef<T> {
|
|
||||||
return getOrCreateTemplateRef<T>(getOrCreateNodeInjector());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
|
|
||||||
* already exists, retrieves the existing ViewContainerRef.
|
|
||||||
*
|
|
||||||
* @returns The ViewContainerRef instance to use
|
|
||||||
*/
|
|
||||||
export function injectViewContainerRef(): ViewContainerRef {
|
|
||||||
return getOrCreateContainerRef(getOrCreateNodeInjector());
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
//// Element
|
//// Element
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
@ -1892,19 +1819,15 @@ function valueInData<T>(data: any[], index: number, value?: T): T {
|
||||||
return value !;
|
return value !;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function query<T>(
|
export function getCurrentQuery(QueryType: {new (): LQuery}): LQuery {
|
||||||
predicate: Type<any>| string[], descend?: boolean,
|
return currentQuery || (currentQuery = new QueryType());
|
||||||
read?: QueryReadType | Type<T>): QueryList<T> {
|
|
||||||
ngDevMode && assertPreviousIsParent();
|
|
||||||
const queryList = new QueryList<T>();
|
|
||||||
const query = currentQuery || (currentQuery = new LQuery_());
|
|
||||||
query.track(queryList, predicate, descend, read);
|
|
||||||
return queryList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPreviousOrParentNode(): LNode {
|
||||||
|
return previousOrParentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertPreviousIsParent() {
|
||||||
function assertPreviousIsParent() {
|
|
||||||
assertEqual(isParent, true, 'isParent');
|
assertEqual(isParent, true, 'isParent');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import {QueryList} from '../../linker';
|
import {QueryList} from '../../linker';
|
||||||
import {Type} from '../../type';
|
import {Type} from '../../type';
|
||||||
|
|
||||||
|
import {LInjector} from './injector';
|
||||||
import {LContainerNode, LNode, LViewNode} from './node';
|
import {LContainerNode, LNode, LViewNode} from './node';
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,15 +49,10 @@ export interface LQuery {
|
||||||
*/
|
*/
|
||||||
track<T>(
|
track<T>(
|
||||||
queryList: QueryList<T>, predicate: Type<any>|string[], descend?: boolean,
|
queryList: QueryList<T>, predicate: Type<any>|string[], descend?: boolean,
|
||||||
read?: QueryReadType|Type<T>): void;
|
read?: QueryReadType<T>|Type<T>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An enum representing possible values of the "read" option for queries. */
|
export class QueryReadType<T> { private defeatStructuralTyping: any; }
|
||||||
export const enum QueryReadType {
|
|
||||||
ElementRef = 0,
|
|
||||||
ViewContainerRef = 1,
|
|
||||||
TemplateRef = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
// Note: This hack is necessary so we don't erroneously get a circular dependency
|
||||||
// failure based on types.
|
// failure based on types.
|
||||||
|
|
|
@ -16,7 +16,8 @@ import {TemplateRef as viewEngine_TemplateRef} from '../linker/template_ref';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {assertNotNull} from './assert';
|
import {assertNotNull} from './assert';
|
||||||
import {getOrCreateContainerRef, getOrCreateElementRef, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from './di';
|
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
|
||||||
|
import {assertPreviousIsParent, getCurrentQuery} from './instructions';
|
||||||
import {DirectiveDef, TypedDirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
import {DirectiveDef, TypedDirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||||
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||||
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode, TNode, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
import {LContainerNode, LElementNode, LNode, LNodeFlags, LViewNode, TNode, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||||
|
@ -26,6 +27,20 @@ import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
||||||
|
|
||||||
|
|
||||||
|
export function query<T>(
|
||||||
|
predicate: Type<any>| string[], descend?: boolean,
|
||||||
|
read?: QueryReadType<T>| Type<T>): QueryList<T> {
|
||||||
|
ngDevMode && assertPreviousIsParent();
|
||||||
|
const queryList = new QueryList<T>();
|
||||||
|
const query = getCurrentQuery(LQuery_);
|
||||||
|
query.track(queryList, predicate, descend, read);
|
||||||
|
return queryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryRefresh(query: QueryList<any>): boolean {
|
||||||
|
return (query as any)._refresh();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A predicate which determines if a given element/directive should be included in the query
|
* A predicate which determines if a given element/directive should be included in the query
|
||||||
*/
|
*/
|
||||||
|
@ -53,7 +68,7 @@ export interface QueryPredicate<T> {
|
||||||
/**
|
/**
|
||||||
* Indicates which token should be read from DI for this query.
|
* Indicates which token should be read from DI for this query.
|
||||||
*/
|
*/
|
||||||
read: QueryReadType|null;
|
read: QueryReadType<T>|Type<T>|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Values which have been located.
|
* Values which have been located.
|
||||||
|
@ -71,7 +86,7 @@ export class LQuery_ implements LQuery {
|
||||||
|
|
||||||
track<T>(
|
track<T>(
|
||||||
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
||||||
read?: QueryReadType): void {
|
read?: QueryReadType<T>|Type<T>): 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) {
|
||||||
|
@ -152,26 +167,13 @@ function geIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readDefaultInjectable(nodeInjector: LInjector, 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(
|
function readFromNodeInjector(
|
||||||
nodeInjector: LInjector, node: LNode, read: QueryReadType | Type<any>): any {
|
nodeInjector: LInjector, node: LNode, read: QueryReadType<any>| Type<any>| null,
|
||||||
if (read === QueryReadType.ElementRef) {
|
directiveIdx: number = -1): any {
|
||||||
return getOrCreateElementRef(nodeInjector);
|
if (read instanceof ReadFromInjectorFn) {
|
||||||
} else if (read === QueryReadType.ViewContainerRef) {
|
return read.read(nodeInjector, node, directiveIdx);
|
||||||
return getOrCreateContainerRef(nodeInjector);
|
|
||||||
} else if (read === QueryReadType.TemplateRef) {
|
|
||||||
return getOrCreateTemplateRef(nodeInjector);
|
|
||||||
} else {
|
} else {
|
||||||
const matchingIdx = geIdxOfMatchingDirective(node, read);
|
const matchingIdx = geIdxOfMatchingDirective(node, read as Type<any>);
|
||||||
if (matchingIdx !== null) {
|
if (matchingIdx !== null) {
|
||||||
return node.view.data[matchingIdx];
|
return node.view.data[matchingIdx];
|
||||||
}
|
}
|
||||||
|
@ -203,17 +205,12 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||||
// is anything on a node matching a selector?
|
// is anything on a node matching a selector?
|
||||||
if (directiveIdx !== null) {
|
if (directiveIdx !== null) {
|
||||||
if (predicate.read !== null) {
|
if (predicate.read !== null) {
|
||||||
const requestedRead = readFromNodeInjector(nodeInjector, node, predicate.read);
|
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
||||||
if (requestedRead !== null) {
|
if (result !== null) {
|
||||||
predicate.values.push(requestedRead);
|
predicate.values.push(result);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// is local name pointing to a directive?
|
predicate.values.push(node.view.data[directiveIdx]);
|
||||||
if (directiveIdx > -1) {
|
|
||||||
predicate.values.push(node.view.data[directiveIdx]);
|
|
||||||
} else {
|
|
||||||
predicate.values.push(readDefaultInjectable(nodeInjector, node));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,7 +221,7 @@ function add(predicate: QueryPredicate<any>| null, node: LNode) {
|
||||||
|
|
||||||
function createPredicate<T>(
|
function createPredicate<T>(
|
||||||
previous: QueryPredicate<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
previous: QueryPredicate<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
||||||
read: QueryReadType | null): QueryPredicate<T> {
|
read: QueryReadType<T>| Type<T>| 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) {
|
||||||
|
@ -309,7 +306,3 @@ class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||||
// it can't be implemented only extended.
|
// it can't be implemented only extended.
|
||||||
export type QueryList<T> = viewEngine_QueryList<T>;
|
export type QueryList<T> = viewEngine_QueryList<T>;
|
||||||
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
|
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
|
||||||
|
|
||||||
export function queryRefresh(query: QueryList<any>): boolean {
|
|
||||||
return (query as any as QueryList_<any>)._refresh();
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
import {bloomAdd, bloomFindPossibleInjector} from '../../src/render3/di';
|
import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector} from '../../src/render3/di';
|
||||||
import {C, E, PublicFeature, T, V, b, b2, cR, cr, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, m, t, v} from '../../src/render3/index';
|
import {C, E, PublicFeature, T, V, b, b2, cR, cr, defineDirective, e, inject, injectElementRef, injectTemplateRef, injectViewContainerRef, m, t, v} from '../../src/render3/index';
|
||||||
import {createLNode, createLView, enterView, getOrCreateNodeInjector, leaveView} from '../../src/render3/instructions';
|
import {createLNode, createLView, enterView, leaveView} from '../../src/render3/instructions';
|
||||||
import {LInjector} from '../../src/render3/interfaces/injector';
|
import {LInjector} from '../../src/render3/interfaces/injector';
|
||||||
import {LNodeFlags} from '../../src/render3/interfaces/node';
|
import {LNodeFlags} from '../../src/render3/interfaces/node';
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
* 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 {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF} from '../../src/render3/di';
|
||||||
import {C, E, Q, QueryList, e, m, qR} from '../../src/render3/index';
|
import {C, E, Q, QueryList, e, m, qR} from '../../src/render3/index';
|
||||||
import {QueryReadType} from '../../src/render3/interfaces/query';
|
import {QueryReadType} from '../../src/render3/interfaces/query';
|
||||||
|
|
||||||
import {createComponent, createDirective, renderComponent} from './render_util';
|
import {createComponent, createDirective, renderComponent} from './render_util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to check if a given candidate object resembles ElementRef
|
* Helper function to check if a given candidate object resembles ElementRef
|
||||||
* @param candidate
|
* @param candidate
|
||||||
|
@ -91,7 +93,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(Child, false, QueryReadType.ElementRef));
|
m(0, Q(Child, false, QUERY_READ_ELEMENT_REF));
|
||||||
elToQuery = E(1, 'div', null, [Child]);
|
elToQuery = E(1, 'div', null, [Child]);
|
||||||
e();
|
e();
|
||||||
}
|
}
|
||||||
|
@ -173,7 +175,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo']));
|
m(0, Q(['foo'], false, QUERY_READ_ELEMENT_REF));
|
||||||
elToQuery = E(1, 'div', null, null, ['foo', '']);
|
elToQuery = E(1, 'div', null, null, ['foo', '']);
|
||||||
e();
|
e();
|
||||||
E(2, 'div');
|
E(2, 'div');
|
||||||
|
@ -203,7 +205,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo', 'bar']));
|
m(0, Q(['foo', 'bar'], undefined, QUERY_READ_ELEMENT_REF));
|
||||||
el1ToQuery = E(1, 'div', null, null, ['foo', '']);
|
el1ToQuery = E(1, 'div', null, null, ['foo', '']);
|
||||||
e();
|
e();
|
||||||
E(2, 'div');
|
E(2, 'div');
|
||||||
|
@ -234,7 +236,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], false, QueryReadType.ElementRef));
|
m(0, Q(['foo'], false, QUERY_READ_ELEMENT_REF));
|
||||||
elToQuery = E(1, 'div', null, null, ['foo', '']);
|
elToQuery = E(1, 'div', null, null, ['foo', '']);
|
||||||
e();
|
e();
|
||||||
E(2, 'div');
|
E(2, 'div');
|
||||||
|
@ -260,7 +262,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], false, QueryReadType.ViewContainerRef));
|
m(0, Q(['foo'], false, QUERY_READ_CONTAINER_REF));
|
||||||
E(1, 'div', null, null, ['foo', '']);
|
E(1, 'div', null, null, ['foo', '']);
|
||||||
e();
|
e();
|
||||||
}
|
}
|
||||||
|
@ -283,7 +285,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], false, QueryReadType.ViewContainerRef));
|
m(0, Q(['foo'], false, QUERY_READ_CONTAINER_REF));
|
||||||
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
||||||
}
|
}
|
||||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
@ -306,7 +308,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], false, QueryReadType.ElementRef));
|
m(0, Q(['foo'], false, QUERY_READ_ELEMENT_REF));
|
||||||
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
||||||
}
|
}
|
||||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
@ -330,7 +332,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo']));
|
m(0, Q(['foo'], undefined, QUERY_READ_TEMPLATE_REF));
|
||||||
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
||||||
}
|
}
|
||||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
@ -353,7 +355,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], false, QueryReadType.TemplateRef));
|
m(0, Q(['foo'], false, QUERY_READ_TEMPLATE_REF));
|
||||||
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
C(1, undefined, undefined, undefined, undefined, ['foo', '']);
|
||||||
}
|
}
|
||||||
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
qR(tmp = m<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
|
||||||
|
@ -465,7 +467,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo'], undefined, QueryReadType.ElementRef));
|
m(0, Q(['foo'], undefined, QUERY_READ_ELEMENT_REF));
|
||||||
div = E(1, 'div', null, [Child], ['foo', 'child']);
|
div = E(1, 'div', null, [Child], ['foo', 'child']);
|
||||||
e();
|
e();
|
||||||
}
|
}
|
||||||
|
@ -491,7 +493,7 @@ describe('query', () => {
|
||||||
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
const Cmpt = createComponent('cmpt', function(ctx: any, cm: boolean) {
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
if (cm) {
|
if (cm) {
|
||||||
m(0, Q(['foo', 'bar']));
|
m(0, Q(['foo', 'bar'], undefined, QUERY_READ_FROM_NODE));
|
||||||
div = E(1, 'div', null, [Child], ['foo', '', 'bar', 'child']);
|
div = E(1, 'div', null, [Child], ['foo', '', 'bar', 'child']);
|
||||||
{ childInstance = m(2); }
|
{ childInstance = m(2); }
|
||||||
e();
|
e();
|
||||||
|
|
Loading…
Reference in New Issue