refactor(ivy): remove content query creation from directive factories (#24811)
PR Close #24811
This commit is contained in:
parent
328971ffcc
commit
0399c6972a
|
@ -39,6 +39,7 @@ export {
|
||||||
T as ɵT,
|
T as ɵT,
|
||||||
V as ɵV,
|
V as ɵV,
|
||||||
Q as ɵQ,
|
Q as ɵQ,
|
||||||
|
Qr as ɵQr,
|
||||||
d as ɵd,
|
d as ɵd,
|
||||||
P as ɵP,
|
P as ɵP,
|
||||||
b as ɵb,
|
b as ɵb,
|
||||||
|
@ -69,6 +70,7 @@ export {
|
||||||
cR as ɵcR,
|
cR as ɵcR,
|
||||||
cr as ɵcr,
|
cr as ɵcr,
|
||||||
qR as ɵqR,
|
qR as ɵqR,
|
||||||
|
ql as ɵql,
|
||||||
e as ɵe,
|
e as ɵe,
|
||||||
p as ɵp,
|
p as ɵp,
|
||||||
pD as ɵpD,
|
pD as ɵpD,
|
||||||
|
|
|
@ -48,7 +48,7 @@ export function defineComponent<T>(componentDefinition: {
|
||||||
/**
|
/**
|
||||||
* Factory method used to create an instance of directive.
|
* Factory method used to create an instance of directive.
|
||||||
*/
|
*/
|
||||||
factory: () => T | ({0: T} & any[]); /* trying to say T | [T, ...any] */
|
factory: () => T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static attributes to set on host element.
|
* Static attributes to set on host element.
|
||||||
|
@ -120,6 +120,14 @@ export function defineComponent<T>(componentDefinition: {
|
||||||
*/
|
*/
|
||||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to create instances of content queries associated with a given directive.
|
||||||
|
*/
|
||||||
|
contentQueries?: (() => void);
|
||||||
|
|
||||||
|
/** Refreshes content queries associated with directives in a given view */
|
||||||
|
contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the name that can be used in the template to assign this directive to a variable.
|
* Defines the name that can be used in the template to assign this directive to a variable.
|
||||||
*
|
*
|
||||||
|
@ -216,6 +224,8 @@ export function defineComponent<T>(componentDefinition: {
|
||||||
factory: componentDefinition.factory,
|
factory: componentDefinition.factory,
|
||||||
template: componentDefinition.template || null !,
|
template: componentDefinition.template || null !,
|
||||||
hostBindings: componentDefinition.hostBindings || null,
|
hostBindings: componentDefinition.hostBindings || null,
|
||||||
|
contentQueries: componentDefinition.contentQueries || null,
|
||||||
|
contentQueriesRefresh: componentDefinition.contentQueriesRefresh || null,
|
||||||
attributes: componentDefinition.attributes || null,
|
attributes: componentDefinition.attributes || null,
|
||||||
inputs: invertObject(componentDefinition.inputs, declaredInputs),
|
inputs: invertObject(componentDefinition.inputs, declaredInputs),
|
||||||
declaredInputs: declaredInputs,
|
declaredInputs: declaredInputs,
|
||||||
|
@ -448,6 +458,14 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
|
||||||
*/
|
*/
|
||||||
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
hostBindings?: (directiveIndex: number, elementIndex: number) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to create instances of content queries associated with a given directive.
|
||||||
|
*/
|
||||||
|
contentQueries?: (() => void);
|
||||||
|
|
||||||
|
/** Refreshes content queries associated with directives in a given view */
|
||||||
|
contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the name that can be used in the template to assign this directive to a variable.
|
* Defines the name that can be used in the template to assign this directive to a variable.
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
|
||||||
import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getDirectiveInstance, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
|
import {addToViewTree, assertPreviousIsParent, createEmbeddedViewNode, createLContainer, createLNodeObject, createTNode, getPreviousOrParentNode, getRenderer, isComponent, renderEmbeddedTemplate, resolveDirective} from './instructions';
|
||||||
import {VIEWS} from './interfaces/container';
|
import {VIEWS} from './interfaces/container';
|
||||||
import {ComponentTemplate, DirectiveDefInternal, RenderFlags} from './interfaces/definition';
|
import {ComponentTemplate, DirectiveDefInternal, RenderFlags} from './interfaces/definition';
|
||||||
import {LInjector} from './interfaces/injector';
|
import {LInjector} from './interfaces/injector';
|
||||||
|
@ -378,7 +378,7 @@ export function getOrCreateInjectable<T>(
|
||||||
// and matches the given token, return the directive instance.
|
// and matches the given token, return the directive instance.
|
||||||
const directiveDef = defs[i] as DirectiveDefInternal<any>;
|
const directiveDef = defs[i] as DirectiveDefInternal<any>;
|
||||||
if (directiveDef.type === token && directiveDef.diPublic) {
|
if (directiveDef.type === token && directiveDef.diPublic) {
|
||||||
return getDirectiveInstance(node.view[DIRECTIVES] ![i]);
|
return node.view[DIRECTIVES] ![i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,11 @@ export {
|
||||||
query as Q,
|
query as Q,
|
||||||
queryRefresh as qR,
|
queryRefresh as qR,
|
||||||
} from './query';
|
} from './query';
|
||||||
|
export {
|
||||||
|
registerContentQuery as Qr,
|
||||||
|
loadQueryList as ql,
|
||||||
|
} from './instructions';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
pureFunction0 as f0,
|
pureFunction0 as f0,
|
||||||
pureFunction1 as f1,
|
pureFunction1 as f1,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import './ng_dev_mode';
|
import './ng_dev_mode';
|
||||||
|
|
||||||
|
import {QueryList} from '../linker';
|
||||||
import {Sanitizer} from '../sanitization/security';
|
import {Sanitizer} from '../sanitization/security';
|
||||||
|
|
||||||
import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert';
|
import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotEqual} from './assert';
|
||||||
|
@ -20,7 +21,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
|
||||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTEXT, CurrentMatchesList, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
|
@ -29,6 +30,7 @@ import {isDifferent, stringify} from './util';
|
||||||
import {ViewRef} from './view_ref';
|
import {ViewRef} from './view_ref';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive (D) sets a property on all component instances using this constant as a key and the
|
* Directive (D) sets a property on all component instances using this constant as a key and the
|
||||||
* component's host node (LElement) as the value. This is used in methods like detectChanges to
|
* component's host node (LElement) as the value. This is used in methods like detectChanges to
|
||||||
|
@ -266,9 +268,11 @@ function refreshView() {
|
||||||
tView.firstTemplatePass = firstTemplatePass = false;
|
tView.firstTemplatePass = firstTemplatePass = false;
|
||||||
|
|
||||||
setHostBindings(tView.hostBindings);
|
setHostBindings(tView.hostBindings);
|
||||||
|
refreshContentQueries(tView);
|
||||||
refreshChildComponents(tView.components);
|
refreshChildComponents(tView.components);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Sets the host bindings for the current view. */
|
/** Sets the host bindings for the current view. */
|
||||||
export function setHostBindings(bindings: number[] | null): void {
|
export function setHostBindings(bindings: number[] | null): void {
|
||||||
if (bindings != null) {
|
if (bindings != null) {
|
||||||
|
@ -281,6 +285,18 @@ export function setHostBindings(bindings: number[] | null): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Refreshes content queries for all directives in the given view. */
|
||||||
|
function refreshContentQueries(tView: TView): void {
|
||||||
|
if (tView.contentQueries != null) {
|
||||||
|
for (let i = 0; i < tView.contentQueries.length; i += 2) {
|
||||||
|
const directiveDefIdx = tView.contentQueries[i];
|
||||||
|
const directiveDef = tView.directives ![directiveDefIdx];
|
||||||
|
|
||||||
|
directiveDef.contentQueriesRefresh !(directiveDefIdx, tView.contentQueries[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Refreshes child components in the current view. */
|
/** Refreshes child components in the current view. */
|
||||||
function refreshChildComponents(components: number[] | null): void {
|
function refreshChildComponents(components: number[] | null): void {
|
||||||
if (components != null) {
|
if (components != null) {
|
||||||
|
@ -315,7 +331,8 @@ export function createLViewData<T>(
|
||||||
renderer, // renderer
|
renderer, // renderer
|
||||||
sanitizer || null, // sanitizer
|
sanitizer || null, // sanitizer
|
||||||
null, // tail
|
null, // tail
|
||||||
-1 // containerIndex
|
-1, // containerIndex
|
||||||
|
null, // contentQueries
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -882,6 +899,7 @@ export function createTView(
|
||||||
pipeDestroyHooks: null,
|
pipeDestroyHooks: null,
|
||||||
cleanup: null,
|
cleanup: null,
|
||||||
hostBindings: null,
|
hostBindings: null,
|
||||||
|
contentQueries: null,
|
||||||
components: null,
|
components: null,
|
||||||
directiveRegistry: typeof directives === 'function' ? directives() : directives,
|
directiveRegistry: typeof directives === 'function' ? directives() : directives,
|
||||||
pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
|
pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
|
||||||
|
@ -1472,7 +1490,7 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a directive.
|
* Create a directive and their associated content queries.
|
||||||
*
|
*
|
||||||
* NOTE: directives can be created in order other than the index order. They can also
|
* NOTE: directives can be created in order other than the index order. They can also
|
||||||
* be retrieved before they are created in which case the value will be null.
|
* be retrieved before they are created in which case the value will be null.
|
||||||
|
@ -1481,28 +1499,32 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
|
||||||
* @param directiveDef DirectiveDef object which contains information about the template.
|
* @param directiveDef DirectiveDef object which contains information about the template.
|
||||||
*/
|
*/
|
||||||
export function directiveCreate<T>(
|
export function directiveCreate<T>(
|
||||||
index: number, directive: T,
|
directiveDefIdx: number, directive: T,
|
||||||
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T {
|
directiveDef: DirectiveDefInternal<T>| ComponentDefInternal<T>): T {
|
||||||
const instance = baseDirectiveCreate(index, directive, directiveDef);
|
const instance = baseDirectiveCreate(directiveDefIdx, directive, directiveDef);
|
||||||
|
|
||||||
ngDevMode && assertDefined(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
ngDevMode && assertDefined(previousOrParentNode.tNode, 'previousOrParentNode.tNode');
|
||||||
const tNode = previousOrParentNode.tNode;
|
const tNode = previousOrParentNode.tNode;
|
||||||
|
|
||||||
const isComponent = (directiveDef as ComponentDefInternal<T>).template;
|
const isComponent = (directiveDef as ComponentDefInternal<T>).template;
|
||||||
if (isComponent) {
|
if (isComponent) {
|
||||||
addComponentLogic(index, directive, directiveDef as ComponentDefInternal<T>);
|
addComponentLogic(directiveDefIdx, directive, directiveDef as ComponentDefInternal<T>);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstTemplatePass) {
|
if (firstTemplatePass) {
|
||||||
// Init hooks are queued now so ngOnInit is called in host components before
|
// Init hooks are queued now so ngOnInit is called in host components before
|
||||||
// any projected components.
|
// any projected components.
|
||||||
queueInitHooks(index, directiveDef.onInit, directiveDef.doCheck, tView);
|
queueInitHooks(directiveDefIdx, directiveDef.onInit, directiveDef.doCheck, tView);
|
||||||
|
|
||||||
if (directiveDef.hostBindings) queueHostBindingForCheck(index);
|
if (directiveDef.hostBindings) queueHostBindingForCheck(directiveDefIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tNode && tNode.attrs) {
|
if (tNode && tNode.attrs) {
|
||||||
setInputsFromAttrs(index, instance, directiveDef.inputs, tNode);
|
setInputsFromAttrs(directiveDefIdx, instance, directiveDef.inputs, tNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (directiveDef.contentQueries) {
|
||||||
|
directiveDef.contentQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
|
@ -1918,7 +1940,7 @@ export function componentRefresh<T>(directiveIndex: number, adjustedElementIndex
|
||||||
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
// Only attached CheckAlways components or attached, dirty OnPush components should be checked
|
||||||
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
if (viewAttached(hostView) && hostView[FLAGS] & (LViewFlags.CheckAlways | LViewFlags.Dirty)) {
|
||||||
ngDevMode && assertDataInRange(directiveIndex, directives !);
|
ngDevMode && assertDataInRange(directiveIndex, directives !);
|
||||||
detectChangesInternal(hostView, element, getDirectiveInstance(directives ![directiveIndex]));
|
detectChangesInternal(hostView, element, directives ![directiveIndex]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2537,6 +2559,15 @@ export function loadDirective<T>(index: number): T {
|
||||||
return directives ![index];
|
return directives ![index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loadQueryList<T>(queryListIdx: number): QueryList<T> {
|
||||||
|
ngDevMode && assertDefined(
|
||||||
|
viewData[CONTENT_QUERIES],
|
||||||
|
'Content QueryList array should be defined if reading a query.');
|
||||||
|
ngDevMode && assertDataInRange(queryListIdx, viewData[CONTENT_QUERIES] !);
|
||||||
|
|
||||||
|
return viewData[CONTENT_QUERIES] ![queryListIdx];
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets the current binding value and increments the binding index. */
|
/** Gets the current binding value and increments the binding index. */
|
||||||
export function consumeBinding(): any {
|
export function consumeBinding(): any {
|
||||||
ngDevMode && assertDataInRange(viewData[BINDING_INDEX]);
|
ngDevMode && assertDataInRange(viewData[BINDING_INDEX]);
|
||||||
|
@ -2586,10 +2617,22 @@ export function getTView(): TView {
|
||||||
return tView;
|
return tView;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDirectiveInstance<T>(instanceOrArray: T | [T]): T {
|
/**
|
||||||
// Directives with content queries store an array in directives[directiveIndex]
|
* Registers a QueryList, associated with a content query, for later refresh (part of a view
|
||||||
// with the instance as the first index
|
* refresh).
|
||||||
return Array.isArray(instanceOrArray) ? instanceOrArray[0] : instanceOrArray;
|
*/
|
||||||
|
export function registerContentQuery<Q>(queryList: QueryList<Q>): void {
|
||||||
|
const savedContentQueriesLength =
|
||||||
|
(viewData[CONTENT_QUERIES] || (viewData[CONTENT_QUERIES] = [])).push(queryList);
|
||||||
|
if (firstTemplatePass) {
|
||||||
|
const currentDirectiveIndex = directives !.length - 1;
|
||||||
|
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
|
||||||
|
const lastSavedDirectiveIndex =
|
||||||
|
tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 2] : -1;
|
||||||
|
if (currentDirectiveIndex !== lastSavedDirectiveIndex) {
|
||||||
|
tViewContentQueries.push(currentDirectiveIndex, savedContentQueriesLength - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertPreviousIsParent() {
|
export function assertPreviousIsParent() {
|
||||||
|
|
|
@ -122,6 +122,14 @@ export interface DirectiveDef<T, Selector extends string> {
|
||||||
*/
|
*/
|
||||||
factory(): T|[T];
|
factory(): T|[T];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to create instances of content queries associated with a given directive.
|
||||||
|
*/
|
||||||
|
contentQueries: (() => void)|null;
|
||||||
|
|
||||||
|
/** Refreshes content queries associated with directives in a given view */
|
||||||
|
contentQueriesRefresh: ((directiveIndex: number, queryIndex: number) => void)|null;
|
||||||
|
|
||||||
/** Refreshes host bindings on the associated directive. */
|
/** Refreshes host bindings on the associated directive. */
|
||||||
hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null;
|
hostBindings: ((directiveIndex: number, elementIndex: number) => void)|null;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
|
import {QueryList} from '../../linker';
|
||||||
import {Sanitizer} from '../../sanitization/security';
|
import {Sanitizer} from '../../sanitization/security';
|
||||||
|
|
||||||
import {LContainer} from './container';
|
import {LContainer} from './container';
|
||||||
|
@ -16,7 +17,7 @@ import {LQueries} from './query';
|
||||||
import {Renderer3} from './renderer';
|
import {Renderer3} from './renderer';
|
||||||
|
|
||||||
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
|
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
|
||||||
export const HEADER_OFFSET = 15;
|
export const HEADER_OFFSET = 16;
|
||||||
|
|
||||||
// Below are constants for LViewData indices to help us look up LViewData members
|
// Below are constants for LViewData indices to help us look up LViewData members
|
||||||
// without having to remember the specific indices.
|
// without having to remember the specific indices.
|
||||||
|
@ -36,6 +37,7 @@ export const RENDERER = 11;
|
||||||
export const SANITIZER = 12;
|
export const SANITIZER = 12;
|
||||||
export const TAIL = 13;
|
export const TAIL = 13;
|
||||||
export const CONTAINER_INDEX = 14;
|
export const CONTAINER_INDEX = 14;
|
||||||
|
export const CONTENT_QUERIES = 15;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `LViewData` stores all of the information needed to process the instructions as
|
* `LViewData` stores all of the information needed to process the instructions as
|
||||||
|
@ -153,6 +155,13 @@ export interface LViewData extends Array<any> {
|
||||||
* in multiple containers, so the parent cannot be shared between view instances).
|
* in multiple containers, so the parent cannot be shared between view instances).
|
||||||
*/
|
*/
|
||||||
[CONTAINER_INDEX]: number;
|
[CONTAINER_INDEX]: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores QueryLists associated with content queries of a directive. This data structure is
|
||||||
|
* filled-in as part of a directive creation process and is later used to retrieve a QueryList to
|
||||||
|
* be refreshed.
|
||||||
|
*/
|
||||||
|
[CONTENT_QUERIES]: QueryList<any>[]|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flags associated with an LView (saved in LViewData[FLAGS]) */
|
/** Flags associated with an LView (saved in LViewData[FLAGS]) */
|
||||||
|
@ -413,6 +422,15 @@ export interface TView {
|
||||||
* they will be fed into instructions that expect the raw index (e.g. elementProperty)
|
* they will be fed into instructions that expect the raw index (e.g. elementProperty)
|
||||||
*/
|
*/
|
||||||
hostBindings: number[]|null;
|
hostBindings: number[]|null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of indices for child directives that have content queries.
|
||||||
|
*
|
||||||
|
* Even indices: Directive indices
|
||||||
|
* Odd indices: Starting index of content queries (stored in CONTENT_QUERIES) for this directive
|
||||||
|
*/
|
||||||
|
contentQueries: number[]|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -158,9 +158,6 @@
|
||||||
{
|
{
|
||||||
"name": "getChildLNode"
|
"name": "getChildLNode"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getDirectiveInstance"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getLViewChild"
|
"name": "getLViewChild"
|
||||||
},
|
},
|
||||||
|
@ -197,6 +194,9 @@
|
||||||
{
|
{
|
||||||
"name": "refreshChildComponents"
|
"name": "refreshChildComponents"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "refreshContentQueries"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "refreshDynamicEmbeddedViews"
|
"name": "refreshDynamicEmbeddedViews"
|
||||||
},
|
},
|
||||||
|
|
|
@ -446,9 +446,6 @@
|
||||||
{
|
{
|
||||||
"name": "getCurrentSanitizer"
|
"name": "getCurrentSanitizer"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getDirectiveInstance"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getLViewChild"
|
"name": "getLViewChild"
|
||||||
},
|
},
|
||||||
|
@ -632,6 +629,9 @@
|
||||||
{
|
{
|
||||||
"name": "refreshChildComponents"
|
"name": "refreshChildComponents"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "refreshContentQueries"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "refreshDynamicEmbeddedViews"
|
"name": "refreshDynamicEmbeddedViews"
|
||||||
},
|
},
|
||||||
|
|
|
@ -108,18 +108,17 @@ describe('queries', () => {
|
||||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
type: ContentQueryComponent,
|
type: ContentQueryComponent,
|
||||||
selectors: [['content-query-component']],
|
selectors: [['content-query-component']],
|
||||||
factory: function ContentQueryComponent_Factory() {
|
factory: function ContentQueryComponent_Factory() { return new ContentQueryComponent(); },
|
||||||
return [
|
contentQueries: function ContentQueryComponent_ContentQueries() {
|
||||||
new ContentQueryComponent(), $r3$.ɵQ(null, SomeDirective, false),
|
$r3$.ɵQr($r3$.ɵQ(null, SomeDirective, false));
|
||||||
$r3$.ɵQ(null, SomeDirective, false)
|
$r3$.ɵQr($r3$.ɵQ(null, SomeDirective, false));
|
||||||
];
|
|
||||||
},
|
},
|
||||||
hostBindings: function ContentQueryComponent_HostBindings(
|
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(
|
||||||
dirIndex: $number$, elIndex: $number$) {
|
dirIndex: $number$, queryStartIndex: $number$) {
|
||||||
let $tmp$: any;
|
let $tmp$: any;
|
||||||
const $instance$ = $r3$.ɵd<any[]>(dirIndex)[0];
|
const $instance$ = $r3$.ɵd<ContentQueryComponent>(dirIndex);
|
||||||
$r3$.ɵqR($tmp$ = $r3$.ɵd<any[]>(dirIndex)[1]) && ($instance$.someDir = $tmp$.first);
|
$r3$.ɵqR($tmp$ = $r3$.ɵql<any>(queryStartIndex)) && ($instance$.someDir = $tmp$.first);
|
||||||
$r3$.ɵqR($tmp$ = $r3$.ɵd<any[]>(dirIndex)[2]) && ($instance$.someDirList = $tmp$);
|
$r3$.ɵqR($tmp$ = $r3$.ɵql<any>(queryStartIndex + 1)) && ($instance$.someDirList = $tmp$);
|
||||||
},
|
},
|
||||||
template: function ContentQueryComponent_Template(
|
template: function ContentQueryComponent_Template(
|
||||||
rf: $number$, ctx: $ContentQueryComponent$) {
|
rf: $number$, ctx: $ContentQueryComponent$) {
|
||||||
|
@ -153,7 +152,7 @@ describe('queries', () => {
|
||||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, 'content-query-component');
|
$r3$.ɵE(0, 'content-query-component');
|
||||||
contentQueryComp = $r3$.ɵd<any[]>(0)[0];
|
contentQueryComp = $r3$.ɵd<ContentQueryComponent>(0);
|
||||||
$r3$.ɵEe(1, 'div', $e2_attrs$);
|
$r3$.ɵEe(1, 'div', $e2_attrs$);
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
import {EventEmitter} from '../..';
|
import {EventEmitter} from '../..';
|
||||||
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
||||||
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectViewContainerRef} from '../../src/render3/index';
|
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectViewContainerRef} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {query, queryRefresh} from '../../src/render3/query';
|
import {query, queryRefresh} from '../../src/render3/query';
|
||||||
|
|
||||||
|
@ -1684,6 +1684,48 @@ describe('query', () => {
|
||||||
|
|
||||||
describe('content', () => {
|
describe('content', () => {
|
||||||
|
|
||||||
|
it('should support content queries for directives', () => {
|
||||||
|
let withContentInstance: WithContentDirective;
|
||||||
|
|
||||||
|
class WithContentDirective {
|
||||||
|
// @ContentChildren('foo') foos;
|
||||||
|
// TODO(issue/24571): remove '!'.
|
||||||
|
foos !: QueryList<ElementRef>;
|
||||||
|
|
||||||
|
static ngComponentDef = defineDirective({
|
||||||
|
type: WithContentDirective,
|
||||||
|
selectors: [['', 'with-content', '']],
|
||||||
|
factory: () => new WithContentDirective(),
|
||||||
|
contentQueries:
|
||||||
|
() => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); },
|
||||||
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
|
let tmp: any;
|
||||||
|
withContentInstance = loadDirective<WithContentDirective>(dirIndex);
|
||||||
|
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||||
|
(withContentInstance.foos = tmp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <div with-content>
|
||||||
|
* <span #foo></span>
|
||||||
|
* </div>
|
||||||
|
* class Cmpt {
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div', [AttributeMarker.SelectOnly, 'with-content']);
|
||||||
|
{ element(1, 'span', null, ['foo', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
}, [WithContentDirective]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(AppComponent);
|
||||||
|
expect(withContentInstance !.foos.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
|
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
|
||||||
it('should support view and content queries matching the same element', () => {
|
it('should support view and content queries matching the same element', () => {
|
||||||
let withContentComponentInstance: WithContentComponent;
|
let withContentComponentInstance: WithContentComponent;
|
||||||
|
@ -1696,18 +1738,17 @@ describe('query', () => {
|
||||||
static ngComponentDef = defineComponent({
|
static ngComponentDef = defineComponent({
|
||||||
type: WithContentComponent,
|
type: WithContentComponent,
|
||||||
selectors: [['with-content']],
|
selectors: [['with-content']],
|
||||||
factory: () => {
|
factory: () => new WithContentComponent(),
|
||||||
return [new WithContentComponent(), query(null, ['foo'], true, QUERY_READ_FROM_NODE)];
|
contentQueries:
|
||||||
},
|
() => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); },
|
||||||
template: (rf: RenderFlags, ctx: WithContentComponent) => {
|
template: (rf: RenderFlags, ctx: WithContentComponent) => {
|
||||||
// intentionally left empty, don't need anything for this test
|
// intentionally left empty, don't need anything for this test
|
||||||
},
|
},
|
||||||
hostBindings: function ContentQueryComponent_HostBindings(
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
dirIndex: number, elIndex: number) {
|
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
withContentComponentInstance = loadDirective<any[]>(dirIndex)[0];
|
withContentComponentInstance = loadDirective<WithContentComponent>(dirIndex);
|
||||||
queryRefresh(tmp = loadDirective<any[]>(dirIndex)[1]) &&
|
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||||
(withContentComponentInstance.foos = tmp as QueryList<any>);
|
(withContentComponentInstance.foos = tmp);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1760,19 +1801,17 @@ describe('query', () => {
|
||||||
type: QueryDirective,
|
type: QueryDirective,
|
||||||
selectors: [['', 'query', '']],
|
selectors: [['', 'query', '']],
|
||||||
exportAs: 'query',
|
exportAs: 'query',
|
||||||
factory: () => {
|
factory: () => new QueryDirective(),
|
||||||
|
contentQueries: () => {
|
||||||
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
|
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
|
||||||
// QueryList<ElementRef>;
|
// QueryList<ElementRef>;
|
||||||
return [
|
registerContentQuery(query(null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE));
|
||||||
new QueryDirective(), query(null, ['foo', 'bar', 'baz'], true, QUERY_READ_FROM_NODE)
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
hostBindings: function ContentQueryComponent_HostBindings(
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
dirIndex: number, elIndex: number) {
|
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
const instance = loadDirective<any[]>(dirIndex)[0];
|
const instance = loadDirective<QueryDirective>(dirIndex);
|
||||||
queryRefresh(tmp = loadDirective<any[]>(dirIndex)[1]) &&
|
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||||
(instance.fooBars = tmp as QueryList<any>);
|
(instance.fooBars = tmp);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1804,8 +1843,8 @@ describe('query', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
outInstance = (<any>load(1))[0] as QueryDirective;
|
outInstance = load<QueryDirective>(1);
|
||||||
inInstance = (<any>load(5))[0] as QueryDirective;
|
inInstance = load<QueryDirective>(5);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[QueryDirective]);
|
[QueryDirective]);
|
||||||
|
@ -1823,18 +1862,16 @@ describe('query', () => {
|
||||||
type: ShallowQueryDirective,
|
type: ShallowQueryDirective,
|
||||||
selectors: [['', 'shallow-query', '']],
|
selectors: [['', 'shallow-query', '']],
|
||||||
exportAs: 'shallow-query',
|
exportAs: 'shallow-query',
|
||||||
factory: () => {
|
factory: () => new ShallowQueryDirective(),
|
||||||
// @ContentChildren('foo', {descendants: false}) fooBars: QueryList<ElementRef>;
|
contentQueries: () => {
|
||||||
return [
|
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
|
||||||
new ShallowQueryDirective(), query(null, ['foo'], false, QUERY_READ_FROM_NODE)
|
registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE));
|
||||||
];
|
|
||||||
},
|
},
|
||||||
hostBindings: function ContentQueryComponent_HostBindings(
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
dirIndex: number, elIndex: number) {
|
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
const instance = loadDirective<any[]>(dirIndex)[0];
|
const instance = loadDirective<ShallowQueryDirective>(dirIndex);
|
||||||
queryRefresh(tmp = loadDirective<any[]>(dirIndex)[1]) &&
|
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||||
(instance.foos = tmp as QueryList<any>);
|
(instance.foos = tmp);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1845,16 +1882,16 @@ describe('query', () => {
|
||||||
type: DeepQueryDirective,
|
type: DeepQueryDirective,
|
||||||
selectors: [['', 'deep-query', '']],
|
selectors: [['', 'deep-query', '']],
|
||||||
exportAs: 'deep-query',
|
exportAs: 'deep-query',
|
||||||
factory: () => {
|
factory: () => new DeepQueryDirective(),
|
||||||
// @ContentChildren('foo', {descendants: true}) fooBars: QueryList<ElementRef>;
|
contentQueries: () => {
|
||||||
return [new DeepQueryDirective(), query(null, ['foo'], true, QUERY_READ_FROM_NODE)];
|
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
|
||||||
|
registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE));
|
||||||
},
|
},
|
||||||
hostBindings: function ContentQueryComponent_HostBindings(
|
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||||
dirIndex: number, elIndex: number) {
|
|
||||||
let tmp: any;
|
let tmp: any;
|
||||||
const instance = loadDirective<any[]>(dirIndex)[0];
|
const instance = loadDirective<DeepQueryDirective>(dirIndex);
|
||||||
queryRefresh(tmp = loadDirective<any[]>(dirIndex)[1]) &&
|
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||||
(instance.foos = tmp as QueryList<any>);
|
(instance.foos = tmp);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1878,8 +1915,8 @@ describe('query', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
shallowInstance = (<any>load(1))[0] as ShallowQueryDirective;
|
shallowInstance = load<ShallowQueryDirective>(1);
|
||||||
deepInstance = (<any>load(2))[0] as DeepQueryDirective;
|
deepInstance = load<DeepQueryDirective>(2);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[ShallowQueryDirective, DeepQueryDirective]);
|
[ShallowQueryDirective, DeepQueryDirective]);
|
||||||
|
|
Loading…
Reference in New Issue