perf(ivy): match query results on the TView level (#31489)
PR Close #31489
This commit is contained in:
parent
9c954ebc62
commit
d52ae7cbab
|
@ -119,7 +119,7 @@ export class QueryList<T>/* implements Iterable<T> */ {
|
||||||
* on change detection, it will not notify of changes to the queries, unless a new change
|
* on change detection, it will not notify of changes to the queries, unless a new change
|
||||||
* occurs.
|
* occurs.
|
||||||
*
|
*
|
||||||
* @param resultsTree The results tree to store
|
* @param resultsTree The query results to store
|
||||||
*/
|
*/
|
||||||
reset(resultsTree: Array<T|any[]>): void {
|
reset(resultsTree: Array<T|any[]>): void {
|
||||||
this._results = flatten(resultsTree);
|
this._results = flatten(resultsTree);
|
||||||
|
|
|
@ -69,3 +69,7 @@ export function assertLView(value: any) {
|
||||||
assertDefined(value, 'LView must be defined');
|
assertDefined(value, 'LView must be defined');
|
||||||
assertEqual(isLView(value), true, 'Expecting LView');
|
assertEqual(isLView(value), true, 'Expecting LView');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertFirstTemplatePass(tView: TView, errMessage: string) {
|
||||||
|
assertEqual(tView.firstTemplatePass, true, errMessage);
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
import {Type} from '../core';
|
import {Type} from '../core';
|
||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
import {Sanitizer} from '../sanitization/security';
|
import {Sanitizer} from '../sanitization/security';
|
||||||
import {assertDataInRange, assertEqual} from '../util/assert';
|
import {assertDataInRange} from '../util/assert';
|
||||||
|
|
||||||
import {assertComponentType} from './assert';
|
import {assertComponentType} from './assert';
|
||||||
import {getComponentDef} from './definition';
|
import {getComponentDef} from './definition';
|
||||||
|
|
|
@ -12,13 +12,14 @@ import {executePreOrderHooks, registerPostOrderHooks} from '../hooks';
|
||||||
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
|
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
|
||||||
import {ComponentTemplate} from '../interfaces/definition';
|
import {ComponentTemplate} from '../interfaces/definition';
|
||||||
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType} from '../interfaces/node';
|
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType} from '../interfaces/node';
|
||||||
import {BINDING_INDEX, HEADER_OFFSET, LView, QUERIES, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
||||||
import {assertNodeType} from '../node_assert';
|
import {assertNodeType} from '../node_assert';
|
||||||
import {appendChild, removeView} from '../node_manipulation';
|
import {appendChild, removeView} from '../node_manipulation';
|
||||||
import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
||||||
import {getNativeByTNode, loadInternal} from '../util/view_utils';
|
import {getNativeByTNode, loadInternal} from '../util/view_utils';
|
||||||
|
|
||||||
import {addToViewTree, createDirectivesAndLocals, createLContainer, createTView, getOrCreateTNode} from './shared';
|
import {addToViewTree, createDirectivesAndLocals, createLContainer, createTView, getOrCreateTNode, resolveDirectives} from './shared';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,7 +40,6 @@ export function ɵɵcontainer(index: number): void {
|
||||||
if (lView[TVIEW].firstTemplatePass) {
|
if (lView[TVIEW].firstTemplatePass) {
|
||||||
tNode.tViews = [];
|
tNode.tViews = [];
|
||||||
}
|
}
|
||||||
addTContainerToQueries(lView, tNode);
|
|
||||||
setIsNotParent();
|
setIsNotParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +72,18 @@ export function ɵɵtemplate(
|
||||||
// TODO: consider a separate node type for templates
|
// TODO: consider a separate node type for templates
|
||||||
const tContainerNode = containerInternal(lView, index, tagName || null, attrs || null);
|
const tContainerNode = containerInternal(lView, index, tagName || null, attrs || null);
|
||||||
if (tView.firstTemplatePass) {
|
if (tView.firstTemplatePass) {
|
||||||
tContainerNode.tViews = createTView(
|
ngDevMode && ngDevMode.firstTemplatePass++;
|
||||||
|
resolveDirectives(tView, lView, tContainerNode, localRefs || null);
|
||||||
|
|
||||||
|
const embeddedTView = tContainerNode.tViews = createTView(
|
||||||
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
|
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
|
||||||
|
if (tView.queries !== null) {
|
||||||
|
tView.queries.template(tView, tContainerNode);
|
||||||
|
embeddedTView.queries = tView.queries.embeddedTView(tContainerNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createDirectivesAndLocals(tView, lView, tContainerNode, localRefs, localRefExtractor);
|
createDirectivesAndLocals(tView, lView, tContainerNode, localRefExtractor);
|
||||||
addTContainerToQueries(lView, tContainerNode);
|
|
||||||
attachPatchData(getNativeByTNode(tContainerNode, lView), lView);
|
attachPatchData(getNativeByTNode(tContainerNode, lView), lView);
|
||||||
registerPostOrderHooks(tView, tContainerNode);
|
registerPostOrderHooks(tView, tContainerNode);
|
||||||
setIsNotParent();
|
setIsNotParent();
|
||||||
|
@ -133,32 +139,6 @@ export function ɵɵcontainerRefreshEnd(): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reporting a TContainer node queries is a 2-step process as we need to:
|
|
||||||
* - check if the container node itself is matching (query might match a <ng-template> node);
|
|
||||||
* - prepare room for nodes from views that might be created based on the TemplateRef linked to this
|
|
||||||
* container.
|
|
||||||
*
|
|
||||||
* Those 2 operations need to happen in the specific order (match the container node itself, then
|
|
||||||
* prepare space for nodes from views).
|
|
||||||
*/
|
|
||||||
function addTContainerToQueries(lView: LView, tContainerNode: TContainerNode): void {
|
|
||||||
const queries = lView[QUERIES];
|
|
||||||
if (queries) {
|
|
||||||
const lContainer = lView[tContainerNode.index];
|
|
||||||
if (lContainer[QUERIES]) {
|
|
||||||
// Query container should only exist if it was created through a dynamic view
|
|
||||||
// in a directive constructor. In this case, we must splice the template
|
|
||||||
// matches in before the view matches to ensure query results in embedded views
|
|
||||||
// don't clobber query results on the template node itself.
|
|
||||||
queries.insertNodeBeforeViews(tContainerNode);
|
|
||||||
} else {
|
|
||||||
queries.addNode(tContainerNode);
|
|
||||||
lContainer[QUERIES] = queries.container();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function containerInternal(
|
function containerInternal(
|
||||||
lView: LView, nodeIndex: number, tagName: string | null,
|
lView: LView, nodeIndex: number, tagName: string | null,
|
||||||
attrs: TAttributes | null): TContainerNode {
|
attrs: TAttributes | null): TContainerNode {
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
* 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 {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
|
import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
|
||||||
import {assertHasParent} from '../assert';
|
import {assertHasParent} from '../assert';
|
||||||
import {attachPatchData} from '../context_discovery';
|
import {attachPatchData} from '../context_discovery';
|
||||||
import {registerPostOrderHooks} from '../hooks';
|
import {registerPostOrderHooks} from '../hooks';
|
||||||
import {PropertyAliases, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
|
import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
import {BINDING_INDEX, HEADER_OFFSET, LView, QUERIES, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
import {isContentQueryHost} from '../interfaces/type_checks';
|
||||||
|
import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
||||||
import {assertNodeType} from '../node_assert';
|
import {assertNodeType} from '../node_assert';
|
||||||
import {appendChild} from '../node_manipulation';
|
import {appendChild} from '../node_manipulation';
|
||||||
import {applyOnCreateInstructions} from '../node_util';
|
import {applyOnCreateInstructions} from '../node_util';
|
||||||
|
@ -22,7 +24,7 @@ import {getInitialStylingValue, hasClassInput, hasStyleInput} from '../styling_n
|
||||||
import {setUpAttributes} from '../util/attrs_utils';
|
import {setUpAttributes} from '../util/attrs_utils';
|
||||||
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
||||||
|
|
||||||
import {createDirectivesAndLocals, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, renderInitialStyling, setInputsForProperty} from './shared';
|
import {createDirectivesAndLocals, elementCreate, executeContentQueries, getOrCreateTNode, initializeTNodeInputs, renderInitialStyling, resolveDirectives, setInputsForProperty} from './shared';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +68,6 @@ export function ɵɵelementStart(
|
||||||
renderInitialStyling(renderer, native, tNode);
|
renderInitialStyling(renderer, native, tNode);
|
||||||
|
|
||||||
appendChild(native, tNode, lView);
|
appendChild(native, tNode, lView);
|
||||||
createDirectivesAndLocals(tView, lView, tNode, localRefs);
|
|
||||||
|
|
||||||
// any immediate children of a component or template container must be pre-emptively
|
// any immediate children of a component or template container must be pre-emptively
|
||||||
// monkey-patched with the component view data so that the element can be inspected
|
// monkey-patched with the component view data so that the element can be inspected
|
||||||
|
@ -81,6 +82,9 @@ export function ɵɵelementStart(
|
||||||
// static class values as well. (Note that this will be fixed once map-based `[style]`
|
// static class values as well. (Note that this will be fixed once map-based `[style]`
|
||||||
// and `[class]` bindings work for multiple directives.)
|
// and `[class]` bindings work for multiple directives.)
|
||||||
if (tView.firstTemplatePass) {
|
if (tView.firstTemplatePass) {
|
||||||
|
ngDevMode && ngDevMode.firstTemplatePass++;
|
||||||
|
resolveDirectives(tView, lView, tNode, localRefs || null);
|
||||||
|
|
||||||
const inputData = initializeTNodeInputs(tNode);
|
const inputData = initializeTNodeInputs(tNode);
|
||||||
if (inputData && inputData.hasOwnProperty('class')) {
|
if (inputData && inputData.hasOwnProperty('class')) {
|
||||||
tNode.flags |= TNodeFlags.hasClassInput;
|
tNode.flags |= TNodeFlags.hasClassInput;
|
||||||
|
@ -89,13 +93,13 @@ export function ɵɵelementStart(
|
||||||
if (inputData && inputData.hasOwnProperty('style')) {
|
if (inputData && inputData.hasOwnProperty('style')) {
|
||||||
tNode.flags |= TNodeFlags.hasStyleInput;
|
tNode.flags |= TNodeFlags.hasStyleInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tView.queries !== null) {
|
||||||
|
tView.queries.elementStart(tView, tNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentQueries = lView[QUERIES];
|
createDirectivesAndLocals(tView, lView, tNode);
|
||||||
if (currentQueries) {
|
|
||||||
currentQueries.addNode(tNode);
|
|
||||||
lView[QUERIES] = currentQueries.clone(tNode);
|
|
||||||
}
|
|
||||||
executeContentQueries(tView, tNode, lView);
|
executeContentQueries(tView, tNode, lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,15 +127,16 @@ export function ɵɵelementEnd(): void {
|
||||||
|
|
||||||
ngDevMode && assertNodeType(tNode, TNodeType.Element);
|
ngDevMode && assertNodeType(tNode, TNodeType.Element);
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const currentQueries = lView[QUERIES];
|
const tView = lView[TVIEW];
|
||||||
// Go back up to parent queries only if queries have been cloned on this element.
|
|
||||||
if (currentQueries && tNode.index === currentQueries.nodeIndex) {
|
|
||||||
lView[QUERIES] = currentQueries.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerPostOrderHooks(lView[TVIEW], tNode);
|
registerPostOrderHooks(tView, previousOrParentTNode);
|
||||||
decreaseElementDepthCount();
|
decreaseElementDepthCount();
|
||||||
|
|
||||||
|
if (tView.firstTemplatePass && tView.queries !== null &&
|
||||||
|
isContentQueryHost(previousOrParentTNode)) {
|
||||||
|
tView.queries !.elementEnd(previousOrParentTNode);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasClassInput(tNode) && tNode.classes) {
|
if (hasClassInput(tNode) && tNode.classes) {
|
||||||
setDirectiveStylingInput(tNode.classes, lView, tNode.inputs !['class']);
|
setDirectiveStylingInput(tNode.classes, lView, tNode.inputs !['class']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,15 @@ import {assertHasParent} from '../assert';
|
||||||
import {attachPatchData} from '../context_discovery';
|
import {attachPatchData} from '../context_discovery';
|
||||||
import {registerPostOrderHooks} from '../hooks';
|
import {registerPostOrderHooks} from '../hooks';
|
||||||
import {TAttributes, TNodeType} from '../interfaces/node';
|
import {TAttributes, TNodeType} from '../interfaces/node';
|
||||||
import {BINDING_INDEX, HEADER_OFFSET, QUERIES, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
import {isContentQueryHost} from '../interfaces/type_checks';
|
||||||
|
import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
||||||
import {assertNodeType} from '../node_assert';
|
import {assertNodeType} from '../node_assert';
|
||||||
import {appendChild} from '../node_manipulation';
|
import {appendChild} from '../node_manipulation';
|
||||||
import {applyOnCreateInstructions} from '../node_util';
|
import {applyOnCreateInstructions} from '../node_util';
|
||||||
import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
||||||
import {registerInitialStylingOnTNode} from '../styling_next/instructions';
|
import {registerInitialStylingOnTNode} from '../styling_next/instructions';
|
||||||
|
|
||||||
import {createDirectivesAndLocals, executeContentQueries, getOrCreateTNode} from './shared';
|
import {createDirectivesAndLocals, executeContentQueries, getOrCreateTNode, resolveDirectives} from './shared';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,14 +62,17 @@ export function ɵɵelementContainerStart(
|
||||||
}
|
}
|
||||||
|
|
||||||
appendChild(native, tNode, lView);
|
appendChild(native, tNode, lView);
|
||||||
createDirectivesAndLocals(tView, lView, tNode, localRefs);
|
|
||||||
attachPatchData(native, lView);
|
|
||||||
|
|
||||||
const currentQueries = lView[QUERIES];
|
if (tView.firstTemplatePass) {
|
||||||
if (currentQueries) {
|
ngDevMode && ngDevMode.firstTemplatePass++;
|
||||||
currentQueries.addNode(tNode);
|
resolveDirectives(tView, lView, tNode, localRefs || null);
|
||||||
lView[QUERIES] = currentQueries.clone(tNode);
|
if (tView.queries) {
|
||||||
|
tView.queries.elementStart(tView, tNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createDirectivesAndLocals(tView, lView, tNode);
|
||||||
|
attachPatchData(native, lView);
|
||||||
executeContentQueries(tView, tNode, lView);
|
executeContentQueries(tView, tNode, lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,17 +94,17 @@ export function ɵɵelementContainerEnd(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
|
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
|
||||||
const currentQueries = lView[QUERIES];
|
|
||||||
// Go back up to parent queries only if queries have been cloned on this element.
|
|
||||||
if (currentQueries && previousOrParentTNode.index === currentQueries.nodeIndex) {
|
|
||||||
lView[QUERIES] = currentQueries.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is required for all host-level styling-related instructions to run
|
// this is required for all host-level styling-related instructions to run
|
||||||
// in the correct order
|
// in the correct order
|
||||||
previousOrParentTNode.onElementCreationFns && applyOnCreateInstructions(previousOrParentTNode);
|
previousOrParentTNode.onElementCreationFns && applyOnCreateInstructions(previousOrParentTNode);
|
||||||
|
|
||||||
registerPostOrderHooks(tView, previousOrParentTNode);
|
registerPostOrderHooks(tView, previousOrParentTNode);
|
||||||
|
|
||||||
|
if (tView.firstTemplatePass && tView.queries !== null &&
|
||||||
|
isContentQueryHost(previousOrParentTNode)) {
|
||||||
|
tView.queries.elementEnd(previousOrParentTNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,10 +49,6 @@ export function ɵɵembeddedViewStart(
|
||||||
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
|
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
|
||||||
LViewFlags.CheckAlways, null, null);
|
LViewFlags.CheckAlways, null, null);
|
||||||
|
|
||||||
if (lContainer[QUERIES]) {
|
|
||||||
viewToRender[QUERIES] = lContainer[QUERIES] !.createView();
|
|
||||||
}
|
|
||||||
|
|
||||||
const tParentNode = getIsParent() ? previousOrParentTNode :
|
const tParentNode = getIsParent() ? previousOrParentTNode :
|
||||||
previousOrParentTNode && previousOrParentTNode.parent;
|
previousOrParentTNode && previousOrParentTNode.parent;
|
||||||
assignTViewNodeToLView(viewToRender[TVIEW], tParentNode, viewBlockId, viewToRender);
|
assignTViewNodeToLView(viewToRender[TVIEW], tParentNode, viewBlockId, viewToRender);
|
||||||
|
|
|
@ -10,15 +10,16 @@ import {AttributeMarker, ComponentTemplate} from '..';
|
||||||
import {SchemaMetadata} from '../../core';
|
import {SchemaMetadata} from '../../core';
|
||||||
import {assertDefined} from '../../util/assert';
|
import {assertDefined} from '../../util/assert';
|
||||||
import {createNamedArrayType} from '../../util/named_array_type';
|
import {createNamedArrayType} from '../../util/named_array_type';
|
||||||
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../interfaces/container';
|
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS, NATIVE} from '../interfaces/container';
|
||||||
import {DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition';
|
import {DirectiveDefList, PipeDefList, ViewQueriesFunction} from '../interfaces/definition';
|
||||||
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, TIcu} from '../interfaces/i18n';
|
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, TIcu} from '../interfaces/i18n';
|
||||||
import {PropertyAliases, TContainerNode, TElementNode, TNode as ITNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TViewNode} from '../interfaces/node';
|
import {PropertyAliases, TContainerNode, TElementNode, TNode as ITNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TViewNode} from '../interfaces/node';
|
||||||
import {SelectorFlags} from '../interfaces/projection';
|
import {SelectorFlags} from '../interfaces/projection';
|
||||||
import {LQueries} from '../interfaces/query';
|
import {TQueries} from '../interfaces/query';
|
||||||
import {RComment, RElement, RNode} from '../interfaces/renderer';
|
import {RComment, RElement, RNode} from '../interfaces/renderer';
|
||||||
import {StylingContext} from '../interfaces/styling';
|
import {StylingContext} from '../interfaces/styling';
|
||||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTENT_QUERIES, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, T_HOST} from '../interfaces/view';
|
|
||||||
|
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, T_HOST} from '../interfaces/view';
|
||||||
import {TStylingContext} from '../styling_next/interfaces';
|
import {TStylingContext} from '../styling_next/interfaces';
|
||||||
import {DebugStyling as DebugNewStyling, NodeStylingDebug} from '../styling_next/styling_debug';
|
import {DebugStyling as DebugNewStyling, NodeStylingDebug} from '../styling_next/styling_debug';
|
||||||
import {isStylingContext} from '../styling_next/util';
|
import {isStylingContext} from '../styling_next/util';
|
||||||
|
@ -78,11 +79,11 @@ export const TViewConstructor = class TView implements ITView {
|
||||||
public id: number, //
|
public id: number, //
|
||||||
public blueprint: LView, //
|
public blueprint: LView, //
|
||||||
public template: ComponentTemplate<{}>|null, //
|
public template: ComponentTemplate<{}>|null, //
|
||||||
|
public queries: TQueries|null, //
|
||||||
public viewQuery: ViewQueriesFunction<{}>|null, //
|
public viewQuery: ViewQueriesFunction<{}>|null, //
|
||||||
public node: TViewNode|TElementNode|null, //
|
public node: TViewNode|TElementNode|null, //
|
||||||
public data: TData, //
|
public data: TData, //
|
||||||
public bindingStartIndex: number, //
|
public bindingStartIndex: number, //
|
||||||
public viewQueryStartIndex: number, //
|
|
||||||
public expandoStartIndex: number, //
|
public expandoStartIndex: number, //
|
||||||
public expandoInstructions: ExpandoInstructions|null, //
|
public expandoInstructions: ExpandoInstructions|null, //
|
||||||
public firstTemplatePass: boolean, //
|
public firstTemplatePass: boolean, //
|
||||||
|
@ -287,8 +288,7 @@ export class LViewDebug {
|
||||||
next: toDebug(this._raw_lView[NEXT]),
|
next: toDebug(this._raw_lView[NEXT]),
|
||||||
childTail: toDebug(this._raw_lView[CHILD_TAIL]),
|
childTail: toDebug(this._raw_lView[CHILD_TAIL]),
|
||||||
declarationView: toDebug(this._raw_lView[DECLARATION_VIEW]),
|
declarationView: toDebug(this._raw_lView[DECLARATION_VIEW]),
|
||||||
contentQueries: this._raw_lView[CONTENT_QUERIES],
|
queries: null,
|
||||||
queries: this._raw_lView[QUERIES],
|
|
||||||
tHost: this._raw_lView[T_HOST],
|
tHost: this._raw_lView[T_HOST],
|
||||||
bindingIndex: this._raw_lView[BINDING_INDEX],
|
bindingIndex: this._raw_lView[BINDING_INDEX],
|
||||||
};
|
};
|
||||||
|
@ -361,7 +361,7 @@ export class LContainerDebug {
|
||||||
.map(toDebug as(l: LView) => LViewDebug);
|
.map(toDebug as(l: LView) => LViewDebug);
|
||||||
}
|
}
|
||||||
get parent(): LViewDebug|LContainerDebug|null { return toDebug(this._raw_lContainer[PARENT]); }
|
get parent(): LViewDebug|LContainerDebug|null { return toDebug(this._raw_lContainer[PARENT]); }
|
||||||
get queries(): LQueries|null { return this._raw_lContainer[QUERIES]; }
|
get movedViews(): LView[]|null { return this._raw_lContainer[MOVED_VIEWS]; }
|
||||||
get host(): RElement|RComment|StylingContext|LView { return this._raw_lContainer[HOST]; }
|
get host(): RElement|RComment|StylingContext|LView { return this._raw_lContainer[HOST]; }
|
||||||
get native(): RComment { return this._raw_lContainer[NATIVE]; }
|
get native(): RComment { return this._raw_lContainer[NATIVE]; }
|
||||||
get __other__() {
|
get __other__() {
|
||||||
|
|
|
@ -23,7 +23,6 @@ import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/c
|
||||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, FactoryFn, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, FactoryFn, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
||||||
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
|
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector';
|
||||||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
|
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
|
||||||
import {LQueries} from '../interfaces/query';
|
|
||||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
||||||
import {SanitizerFn} from '../interfaces/sanitization';
|
import {SanitizerFn} from '../interfaces/sanitization';
|
||||||
import {StylingContext} from '../interfaces/styling';
|
import {StylingContext} from '../interfaces/styling';
|
||||||
|
@ -157,16 +156,20 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Refreshes content queries for all directives in the given view. */
|
/** Refreshes all content queries declared by directives in a given view */
|
||||||
function refreshContentQueries(tView: TView, lView: LView): void {
|
function refreshContentQueries(tView: TView, lView: LView): void {
|
||||||
if (tView.contentQueries != null) {
|
const contentQueries = tView.contentQueries;
|
||||||
setCurrentQueryIndex(0);
|
if (contentQueries !== null) {
|
||||||
for (let i = 0; i < tView.contentQueries.length; i++) {
|
for (let i = 0; i < contentQueries.length; i += 2) {
|
||||||
const directiveDefIdx = tView.contentQueries[i];
|
const queryStartIdx = contentQueries[i];
|
||||||
const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>;
|
const directiveDefIdx = contentQueries[i + 1];
|
||||||
ngDevMode &&
|
if (directiveDefIdx !== -1) {
|
||||||
assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
|
const directiveDef = tView.data[directiveDefIdx] as DirectiveDef<any>;
|
||||||
directiveDef.contentQueries !(RenderFlags.Update, lView[directiveDefIdx], directiveDefIdx);
|
ngDevMode &&
|
||||||
|
assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
|
||||||
|
setCurrentQueryIndex(queryStartIdx);
|
||||||
|
directiveDef.contentQueries !(RenderFlags.Update, lView[directiveDefIdx], directiveDefIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,8 +364,7 @@ export function allocExpando(view: LView, numSlotsToAlloc: number) {
|
||||||
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
|
* Such lViewNode will then be renderer with renderEmbeddedTemplate() (see below).
|
||||||
*/
|
*/
|
||||||
export function createEmbeddedViewAndNode<T>(
|
export function createEmbeddedViewAndNode<T>(
|
||||||
tView: TView, context: T, declarationView: LView, queries: LQueries | null,
|
tView: TView, context: T, declarationView: LView, injectorIndex: number): LView {
|
||||||
injectorIndex: number): LView {
|
|
||||||
const _isParent = getIsParent();
|
const _isParent = getIsParent();
|
||||||
const _previousOrParentTNode = getPreviousOrParentTNode();
|
const _previousOrParentTNode = getPreviousOrParentTNode();
|
||||||
setPreviousOrParentTNode(null !, true);
|
setPreviousOrParentTNode(null !, true);
|
||||||
|
@ -370,9 +372,6 @@ export function createEmbeddedViewAndNode<T>(
|
||||||
const lView = createLView(declarationView, tView, context, LViewFlags.CheckAlways, null, null);
|
const lView = createLView(declarationView, tView, context, LViewFlags.CheckAlways, null, null);
|
||||||
lView[DECLARATION_VIEW] = declarationView;
|
lView[DECLARATION_VIEW] = declarationView;
|
||||||
|
|
||||||
if (queries) {
|
|
||||||
lView[QUERIES] = queries.createView();
|
|
||||||
}
|
|
||||||
assignTViewNodeToLView(tView, null, -1, lView);
|
assignTViewNodeToLView(tView, null, -1, lView);
|
||||||
|
|
||||||
if (tView.firstTemplatePass) {
|
if (tView.firstTemplatePass) {
|
||||||
|
@ -513,14 +512,8 @@ export function executeContentQueries(tView: TView, tNode: TNode, lView: LView)
|
||||||
*/
|
*/
|
||||||
export function createDirectivesAndLocals(
|
export function createDirectivesAndLocals(
|
||||||
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode,
|
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode,
|
||||||
localRefs: string[] | null | undefined,
|
|
||||||
localRefExtractor: LocalRefExtractor = getNativeByTNode) {
|
localRefExtractor: LocalRefExtractor = getNativeByTNode) {
|
||||||
if (!getBindingsEnabled()) return;
|
if (!getBindingsEnabled()) return;
|
||||||
if (tView.firstTemplatePass) {
|
|
||||||
ngDevMode && ngDevMode.firstTemplatePass++;
|
|
||||||
resolveDirectives(
|
|
||||||
tView, lView, findDirectiveMatches(tView, lView, tNode), tNode, localRefs || null);
|
|
||||||
}
|
|
||||||
instantiateAllDirectives(tView, lView, tNode);
|
instantiateAllDirectives(tView, lView, tNode);
|
||||||
invokeDirectivesHostBindings(tView, lView, tNode);
|
invokeDirectivesHostBindings(tView, lView, tNode);
|
||||||
saveResolvedLocalsInData(lView, tNode, localRefExtractor);
|
saveResolvedLocalsInData(lView, tNode, localRefExtractor);
|
||||||
|
@ -588,11 +581,11 @@ export function createTView(
|
||||||
viewIndex, // id: number,
|
viewIndex, // id: number,
|
||||||
blueprint, // blueprint: LView,
|
blueprint, // blueprint: LView,
|
||||||
templateFn, // template: ComponentTemplate<{}>|null,
|
templateFn, // template: ComponentTemplate<{}>|null,
|
||||||
|
null, // queries: TQueries|null
|
||||||
viewQuery, // viewQuery: ViewQueriesFunction<{}>|null,
|
viewQuery, // viewQuery: ViewQueriesFunction<{}>|null,
|
||||||
null !, // node: TViewNode|TElementNode|null,
|
null !, // node: TViewNode|TElementNode|null,
|
||||||
cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData,
|
cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData,
|
||||||
bindingStartIndex, // bindingStartIndex: number,
|
bindingStartIndex, // bindingStartIndex: number,
|
||||||
initialViewLength, // viewQueryStartIndex: number,
|
|
||||||
initialViewLength, // expandoStartIndex: number,
|
initialViewLength, // expandoStartIndex: number,
|
||||||
null, // expandoInstructions: ExpandoInstructions|null,
|
null, // expandoInstructions: ExpandoInstructions|null,
|
||||||
true, // firstTemplatePass: boolean,
|
true, // firstTemplatePass: boolean,
|
||||||
|
@ -619,11 +612,11 @@ export function createTView(
|
||||||
id: viewIndex,
|
id: viewIndex,
|
||||||
blueprint: blueprint,
|
blueprint: blueprint,
|
||||||
template: templateFn,
|
template: templateFn,
|
||||||
|
queries: null,
|
||||||
viewQuery: viewQuery,
|
viewQuery: viewQuery,
|
||||||
node: null !,
|
node: null !,
|
||||||
data: blueprint.slice().fill(null, bindingStartIndex),
|
data: blueprint.slice().fill(null, bindingStartIndex),
|
||||||
bindingStartIndex: bindingStartIndex,
|
bindingStartIndex: bindingStartIndex,
|
||||||
viewQueryStartIndex: initialViewLength,
|
|
||||||
expandoStartIndex: initialViewLength,
|
expandoStartIndex: initialViewLength,
|
||||||
expandoInstructions: null,
|
expandoInstructions: null,
|
||||||
firstTemplatePass: true,
|
firstTemplatePass: true,
|
||||||
|
@ -1031,13 +1024,18 @@ export function instantiateRootComponent<T>(
|
||||||
/**
|
/**
|
||||||
* Resolve the matched directives on a node.
|
* Resolve the matched directives on a node.
|
||||||
*/
|
*/
|
||||||
function resolveDirectives(
|
export function resolveDirectives(
|
||||||
tView: TView, viewData: LView, directives: DirectiveDef<any>[] | null, tNode: TNode,
|
tView: TView, lView: LView, tNode: TElementNode | TContainerNode | TElementContainerNode,
|
||||||
localRefs: string[] | null): void {
|
localRefs: string[] | null): void {
|
||||||
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
|
// Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
|
||||||
// tsickle.
|
// tsickle.
|
||||||
ngDevMode && assertEqual(tView.firstTemplatePass, true, 'should run on first template pass only');
|
ngDevMode && assertEqual(tView.firstTemplatePass, true, 'should run on first template pass only');
|
||||||
|
|
||||||
|
if (!getBindingsEnabled()) return;
|
||||||
|
|
||||||
|
const directives: DirectiveDef<any>[]|null = findDirectiveMatches(tView, lView, tNode);
|
||||||
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
const exportsMap: ({[key: string]: number} | null) = localRefs ? {'': -1} : null;
|
||||||
|
|
||||||
if (directives) {
|
if (directives) {
|
||||||
initNodeFlags(tNode, tView.data.length, directives.length);
|
initNodeFlags(tNode, tView.data.length, directives.length);
|
||||||
// When the same token is provided by several directives on the same node, some rules apply in
|
// When the same token is provided by several directives on the same node, some rules apply in
|
||||||
|
@ -1059,7 +1057,7 @@ function resolveDirectives(
|
||||||
const def = directives[i] as DirectiveDef<any>;
|
const def = directives[i] as DirectiveDef<any>;
|
||||||
|
|
||||||
const directiveDefIdx = tView.data.length;
|
const directiveDefIdx = tView.data.length;
|
||||||
baseResolveDirective(tView, viewData, def, def.factory);
|
baseResolveDirective(tView, lView, def, def.factory);
|
||||||
|
|
||||||
saveNameToExportMap(tView.data !.length - 1, def, exportsMap);
|
saveNameToExportMap(tView.data !.length - 1, def, exportsMap);
|
||||||
|
|
||||||
|
@ -1766,8 +1764,8 @@ export function checkView<T>(hostView: LView, component: T) {
|
||||||
|
|
||||||
function executeViewQueryFn<T>(flags: RenderFlags, tView: TView, component: T): void {
|
function executeViewQueryFn<T>(flags: RenderFlags, tView: TView, component: T): void {
|
||||||
const viewQuery = tView.viewQuery;
|
const viewQuery = tView.viewQuery;
|
||||||
if (viewQuery) {
|
if (viewQuery !== null) {
|
||||||
setCurrentQueryIndex(tView.viewQueryStartIndex);
|
setCurrentQueryIndex(0);
|
||||||
viewQuery(flags, component);
|
viewQuery(flags, component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
import {ViewRef} from '../../linker/view_ref';
|
import {ViewRef} from '../../linker/view_ref';
|
||||||
|
|
||||||
import {TNode} from './node';
|
import {TNode} from './node';
|
||||||
import {LQueries} from './query';
|
|
||||||
import {RComment, RElement} from './renderer';
|
import {RComment, RElement} from './renderer';
|
||||||
import {HOST, LView, NEXT, PARENT, QUERIES, T_HOST} from './view';
|
|
||||||
|
import {HOST, LView, NEXT, PARENT, T_HOST} from './view';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,8 +26,15 @@ export const TYPE = 1;
|
||||||
* Uglify will inline these when minifying so there shouldn't be a cost.
|
* Uglify will inline these when minifying so there shouldn't be a cost.
|
||||||
*/
|
*/
|
||||||
export const ACTIVE_INDEX = 2;
|
export const ACTIVE_INDEX = 2;
|
||||||
// PARENT, NEXT, QUERIES and T_HOST are indices 3, 4, 5 and 6.
|
|
||||||
|
// PARENT and NEXT are indices 3 and 4
|
||||||
// As we already have these constants in LView, we don't need to re-create them.
|
// As we already have these constants in LView, we don't need to re-create them.
|
||||||
|
|
||||||
|
export const MOVED_VIEWS = 5;
|
||||||
|
|
||||||
|
// T_HOST is index 6
|
||||||
|
// We already have this constants in LView, we don't need to re-create it.
|
||||||
|
|
||||||
export const NATIVE = 7;
|
export const NATIVE = 7;
|
||||||
export const VIEW_REFS = 8;
|
export const VIEW_REFS = 8;
|
||||||
|
|
||||||
|
@ -84,11 +91,11 @@ export interface LContainer extends Array<any> {
|
||||||
[NEXT]: LView|LContainer|null;
|
[NEXT]: LView|LContainer|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries active for this container - all the views inserted to / removed from
|
* A collection of views created based on the underlying `<ng-template>` element but inserted into
|
||||||
* this container are reported to queries referenced here.
|
* a different `LContainer`. We need to track views created from a given declaration point since
|
||||||
|
* queries collect matches from the embedded view declaration point and _not_ the insertion point.
|
||||||
*/
|
*/
|
||||||
[QUERIES]: LQueries|null; // TODO(misko): This is abuse of `LContainer` since we are storing
|
[MOVED_VIEWS]: LView[]|null;
|
||||||
// `[QUERIES]` in it which are not needed for `LContainer` (only needed for Template)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pointer to the `TNode` which represents the host of the container.
|
* Pointer to the `TNode` which represents the host of the container.
|
||||||
|
|
|
@ -9,99 +9,211 @@
|
||||||
import {Type} from '../../interface/type';
|
import {Type} from '../../interface/type';
|
||||||
import {QueryList} from '../../linker';
|
import {QueryList} from '../../linker';
|
||||||
|
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode} from './node';
|
import {TNode} from './node';
|
||||||
|
import {TView} from './view';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing query metadata extracted from query annotations.
|
||||||
|
*/
|
||||||
|
export interface TQueryMetadata {
|
||||||
|
predicate: Type<any>|string[];
|
||||||
|
descendants: boolean;
|
||||||
|
read: any;
|
||||||
|
isStatic: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/** Used for tracking queries (e.g. ViewChild, ContentChild). */
|
/**
|
||||||
|
* TQuery objects represent all the query-related data that remain the same from one view instance
|
||||||
|
* to another and can be determined on the very first template pass. Most notably TQuery holds all
|
||||||
|
* the matches for a given view.
|
||||||
|
*/
|
||||||
|
export interface TQuery {
|
||||||
|
/**
|
||||||
|
* Query metadata extracted from query annotations.
|
||||||
|
*/
|
||||||
|
metadata: TQueryMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of a query in a declaration view in case of queries propagated to en embedded view, -1
|
||||||
|
* for queries declared in a given view. We are storing this index so we can find a parent query
|
||||||
|
* to clone for an embedded view (when an embedded view is created).
|
||||||
|
*/
|
||||||
|
indexInDeclarationView: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches collected on the first template pass. Each match is a pair of:
|
||||||
|
* - TNode index;
|
||||||
|
* - match index;
|
||||||
|
*
|
||||||
|
* A TNode index can be either:
|
||||||
|
* - a positive number (the most common case) to indicate a matching TNode;
|
||||||
|
* - a negative number to indicate that a given query is crossing a <ng-template> element and
|
||||||
|
* results from views created based on TemplateRef should be inserted at this place.
|
||||||
|
*
|
||||||
|
* A match index is a number used to find an actual value (for a given node) when query results
|
||||||
|
* are materialized. This index can have one of the following values:
|
||||||
|
* - -2 - indicates that we need to read a special token (TemplateRef, ViewContainerRef etc.);
|
||||||
|
* - -1 - indicates that we need to read a default value based on the node type (TemplateRef for
|
||||||
|
* ng-template and ElementRef for other elements);
|
||||||
|
* - a positive number - index of an injectable to be read from the element injector.
|
||||||
|
*/
|
||||||
|
matches: number[]|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag indicating if a given query crosses an <ng-template> element. This flag exists for
|
||||||
|
* performance reasons: we can notice that queries not crossing any <ng-template> elements will
|
||||||
|
* have matches from a given view only (and adapt processing accordingly).
|
||||||
|
*/
|
||||||
|
crossesNgTemplate: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method call when a given query is crossing an element (or element container). This is where a
|
||||||
|
* given TNode is matched against a query predicate.
|
||||||
|
* @param tView
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
elementStart(tView: TView, tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method called when processing the elementEnd instruction - this is mostly useful to determine
|
||||||
|
* if a given content query should match any nodes past this point.
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
elementEnd(tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method called when processing the template instruction. This is where a
|
||||||
|
* given TContainerNode is matched against a query predicate.
|
||||||
|
* @param tView
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
template(tView: TView, tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A query-related method called when an embedded TView is created based on the content of a
|
||||||
|
* <ng-template> element. We call this method to determine if a given query should be propagated
|
||||||
|
* to the embedded view and if so - return a cloned TQuery for this embedded view.
|
||||||
|
* @param tNode
|
||||||
|
* @param childQueryIndex
|
||||||
|
*/
|
||||||
|
embeddedTView(tNode: TNode, childQueryIndex: number): TQuery|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TQueries represent a collection of individual TQuery objects tracked in a given view. Most of the
|
||||||
|
* methods on this interface are simple proxy methods to the corresponding functionality on TQuery.
|
||||||
|
*/
|
||||||
|
export interface TQueries {
|
||||||
|
/**
|
||||||
|
* Adds a new TQuery to a collection of queries tracked in a given view.
|
||||||
|
* @param tQuery
|
||||||
|
*/
|
||||||
|
track(tQuery: TQuery): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a TQuery instance for at the given index in the queries array.
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
getByIndex(index: number): TQuery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of queries tracked in a given view.
|
||||||
|
*/
|
||||||
|
length: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||||
|
* `elementStart` on each and every TQuery.
|
||||||
|
* @param tView
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
elementStart(tView: TView, tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||||
|
* `elementEnd` on each and every TQuery.
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
elementEnd(tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||||
|
* `template` on each and every TQuery.
|
||||||
|
* @param tView
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
template(tView: TView, tNode: TNode): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A proxy method that iterates over all the TQueries in a given TView and calls the corresponding
|
||||||
|
* `embeddedTView` on each and every TQuery.
|
||||||
|
* @param tNode
|
||||||
|
*/
|
||||||
|
embeddedTView(tNode: TNode): TQueries|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface that represents query-related information specific to a view instance. Most notably
|
||||||
|
* it contains:
|
||||||
|
* - materialized query matches;
|
||||||
|
* - a pointer to a QueryList where materialized query results should be reported.
|
||||||
|
*/
|
||||||
|
export interface LQuery<T> {
|
||||||
|
/**
|
||||||
|
* Materialized query matches for a given view only (!). Results are initialized lazily so the
|
||||||
|
* array of matches is set to `null` initially.
|
||||||
|
*/
|
||||||
|
matches: (T|null)[]|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A QueryList where materialized query results should be reported.
|
||||||
|
*/
|
||||||
|
queryList: QueryList<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones an LQuery for an embedded view. A cloned query shares the same `QueryList` but has a
|
||||||
|
* separate collection of materialized matches.
|
||||||
|
*/
|
||||||
|
clone(): LQuery<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an embedded view, impacting results of this query, is inserted or removed.
|
||||||
|
*/
|
||||||
|
setDirty(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lQueries represent a collection of individual LQuery objects tracked in a given view.
|
||||||
|
*/
|
||||||
export interface LQueries {
|
export interface LQueries {
|
||||||
/**
|
/**
|
||||||
* The parent LQueries instance.
|
* A collection of queries tracked in a given view.
|
||||||
*
|
|
||||||
* When there is a content query, a new LQueries instance is created to avoid mutating any
|
|
||||||
* existing LQueries. After we are done searching content children, the parent property allows
|
|
||||||
* us to traverse back up to the original LQueries instance to continue to search for matches
|
|
||||||
* in the main view.
|
|
||||||
*/
|
*/
|
||||||
parent: LQueries|null;
|
queries: LQuery<any>[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the node on which this LQueries instance was created / cloned in a given LView.
|
* A method called when a new embedded view is created. As a result a set of LQueries applicable
|
||||||
*
|
* for a new embedded view is instantiated (cloned) from the declaration view.
|
||||||
* This index is stored to minimize LQueries cloning: we can observe that LQueries can be mutated
|
* @param tView
|
||||||
* only under 2 conditions:
|
|
||||||
* - we are crossing an element that has directives with content queries (new queries are added);
|
|
||||||
* - we are descending into element hierarchy (creating a child element of an existing element)
|
|
||||||
* and the current LQueries object is tracking shallow queries (shallow queries are removed).
|
|
||||||
*
|
|
||||||
* Since LQueries are not cloned systematically we need to know exactly where (on each element)
|
|
||||||
* cloning occurred, so we can properly restore the set of tracked queries when going up the
|
|
||||||
* elements hierarchy.
|
|
||||||
*
|
|
||||||
* Always set to -1 for view queries as view queries are created before we process any node in a
|
|
||||||
* given view.
|
|
||||||
*/
|
*/
|
||||||
nodeIndex: number;
|
createEmbeddedView(tView: TView): LQueries|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ask queries to prepare a copy of itself. This ensures that:
|
* A method called when an embedded view is inserted into a container. As a result all impacted
|
||||||
* - tracking new queries on content nodes doesn't mutate list of queries tracked on a parent
|
* `LQuery` objects (and associated `QueryList`) are marked as dirty.
|
||||||
* node;
|
* @param tView
|
||||||
* - we don't track shallow queries when descending into elements hierarchy.
|
|
||||||
*
|
|
||||||
* We will clone LQueries before constructing content queries
|
|
||||||
*/
|
*/
|
||||||
clone(tNode: TNode): LQueries;
|
insertView(tView: TView): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify `LQueries` that a new `TNode` has been created and needs to be added to query results
|
* A method called when an embedded view is detached from a container. As a result all impacted
|
||||||
* if matching query predicate.
|
* `LQuery` objects (and associated `QueryList`) are marked as dirty.
|
||||||
|
* @param tView
|
||||||
*/
|
*/
|
||||||
addNode(tNode: TElementNode|TContainerNode|TElementContainerNode): void;
|
detachView(tView: TView): void;
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `LQueries` that a new `TNode` has been created and needs to be added to query results
|
|
||||||
* if matching query predicate. This is a special mode invoked if the query container has to
|
|
||||||
* be created out of order (e.g. view created in the constructor of a directive).
|
|
||||||
*/
|
|
||||||
insertNodeBeforeViews(tNode: TElementNode|TContainerNode|TElementContainerNode): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `LQueries` that a new LContainer was added to ivy data structures. As a result we need
|
|
||||||
* to prepare room for views that might be inserted into this container.
|
|
||||||
*/
|
|
||||||
container(): LQueries|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `LQueries` that a new `LView` has been created. As a result we need to prepare room
|
|
||||||
* and collect nodes that match query predicate.
|
|
||||||
*/
|
|
||||||
createView(): LQueries|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `LQueries` that a new `LView` has been added to `LContainer`. As a result all
|
|
||||||
* the matching nodes from this view should be added to container's queries.
|
|
||||||
*/
|
|
||||||
insertView(newViewIndex: number): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify `LQueries` that an `LView` has been removed from `LContainer`. As a result all
|
|
||||||
* the matching nodes from this view should be removed from container's queries.
|
|
||||||
*/
|
|
||||||
removeView(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add additional `QueryList` to track.
|
|
||||||
*
|
|
||||||
* @param queryList `QueryList` to update with changes.
|
|
||||||
* @param predicate Either `Type` or selector array of [key, value] predicates.
|
|
||||||
* @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<any>|string[], descend?: boolean,
|
|
||||||
read?: Type<T>): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
||||||
export const unusedValueExportToPlacateAjd = 1;
|
export const unusedValueExportToPlacateAjd = 1;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import {InjectionToken} from '../../di/injection_token';
|
import {InjectionToken} from '../../di/injection_token';
|
||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
import {Type} from '../../interface/type';
|
import {Type} from '../../interface/type';
|
||||||
import {QueryList} from '../../linker';
|
|
||||||
import {SchemaMetadata} from '../../metadata';
|
import {SchemaMetadata} from '../../metadata';
|
||||||
import {Sanitizer} from '../../sanitization/security';
|
import {Sanitizer} from '../../sanitization/security';
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBin
|
||||||
import {I18nUpdateOpCodes, TI18n} from './i18n';
|
import {I18nUpdateOpCodes, TI18n} from './i18n';
|
||||||
import {TElementNode, TNode, TViewNode} from './node';
|
import {TElementNode, TNode, TViewNode} from './node';
|
||||||
import {PlayerHandler} from './player';
|
import {PlayerHandler} from './player';
|
||||||
import {LQueries} from './query';
|
import {LQueries, TQueries} from './query';
|
||||||
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
import {RElement, Renderer3, RendererFactory3} from './renderer';
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,11 +41,11 @@ export const RENDERER = 12;
|
||||||
export const SANITIZER = 13;
|
export const SANITIZER = 13;
|
||||||
export const CHILD_HEAD = 14;
|
export const CHILD_HEAD = 14;
|
||||||
export const CHILD_TAIL = 15;
|
export const CHILD_TAIL = 15;
|
||||||
export const CONTENT_QUERIES = 16;
|
export const DECLARATION_VIEW = 16;
|
||||||
export const DECLARATION_VIEW = 17;
|
export const DECLARATION_LCONTAINER = 17;
|
||||||
export const PREORDER_HOOK_FLAGS = 18;
|
export const PREORDER_HOOK_FLAGS = 18;
|
||||||
/** Size of LView's header. Necessary to adjust for it when setting slots. */
|
/** Size of LView's header. Necessary to adjust for it when setting slots. */
|
||||||
export const HEADER_OFFSET = 20;
|
export const HEADER_OFFSET = 19;
|
||||||
|
|
||||||
|
|
||||||
// This interface replaces the real LView interface if it is an arg or a
|
// This interface replaces the real LView interface if it is an arg or a
|
||||||
|
@ -179,13 +178,6 @@ export interface LView extends Array<any> {
|
||||||
*/
|
*/
|
||||||
[CHILD_TAIL]: LView|LContainer|null;
|
[CHILD_TAIL]: LView|LContainer|null;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View where this view's template was declared.
|
* View where this view's template was declared.
|
||||||
*
|
*
|
||||||
|
@ -212,6 +204,16 @@ export interface LView extends Array<any> {
|
||||||
*/
|
*/
|
||||||
[DECLARATION_VIEW]: LView|null;
|
[DECLARATION_VIEW]: LView|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A declaration point of embedded views (ones instantiated based on the content of a
|
||||||
|
* <ng-template>), null for other types of views.
|
||||||
|
*
|
||||||
|
* We need to track all embedded views created from a given declaration point so we can prepare
|
||||||
|
* query matches in a proper order (query matches are ordered based on their declaration point and
|
||||||
|
* _not_ the insertion point).
|
||||||
|
*/
|
||||||
|
[DECLARATION_LCONTAINER]: LContainer|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* More flags for this view. See PreOrderHookFlags for more info.
|
* More flags for this view. See PreOrderHookFlags for more info.
|
||||||
*/
|
*/
|
||||||
|
@ -410,17 +412,6 @@ export interface TView {
|
||||||
*/
|
*/
|
||||||
staticContentQueries: boolean;
|
staticContentQueries: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* The index where the viewQueries section of `LView` begins. This section contains
|
|
||||||
* view queries defined for a component/directive.
|
|
||||||
*
|
|
||||||
* We store this start index so we know where the list of view queries starts.
|
|
||||||
* This is required when we invoke view queries at runtime. We invoke queries one by one and
|
|
||||||
* increment query index after each iteration. This information helps us to reset index back to
|
|
||||||
* the beginning of view query list before we invoke view queries again.
|
|
||||||
*/
|
|
||||||
viewQueryStartIndex: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to the first child node located in the view.
|
* A reference to the first child node located in the view.
|
||||||
*/
|
*/
|
||||||
|
@ -550,7 +541,19 @@ export interface TView {
|
||||||
components: number[]|null;
|
components: number[]|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of indices for child directives that have content queries.
|
* A collection of queries tracked in a given view.
|
||||||
|
*/
|
||||||
|
queries: TQueries|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of indices pointing to directives with content queries alongside with the
|
||||||
|
* corresponding
|
||||||
|
* query index. Each entry in this array is a tuple of:
|
||||||
|
* - index of the first content query index declared by a given directive;
|
||||||
|
* - index of a directive.
|
||||||
|
*
|
||||||
|
* We are storing those indexes so we can refresh content queries as part of a view refresh
|
||||||
|
* process.
|
||||||
*/
|
*/
|
||||||
contentQueries: number[]|null;
|
contentQueries: number[]|null;
|
||||||
|
|
||||||
|
|
|
@ -11,18 +11,18 @@ import {assertDefined, assertDomNode} from '../util/assert';
|
||||||
|
|
||||||
import {assertLContainer, assertLView} from './assert';
|
import {assertLContainer, assertLView} from './assert';
|
||||||
import {attachPatchData} from './context_discovery';
|
import {attachPatchData} from './context_discovery';
|
||||||
import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
import {CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS, NATIVE, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||||
import {ComponentDef} from './interfaces/definition';
|
import {ComponentDef} from './interfaces/definition';
|
||||||
import {NodeInjectorFactory} from './interfaces/injector';
|
import {NodeInjectorFactory} from './interfaces/injector';
|
||||||
import {TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
import {TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||||
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection';
|
||||||
import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
import {ProceduralRenderer3, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||||
import {isLContainer, isLView, isRootView} from './interfaces/type_checks';
|
import {isLContainer, isLView, isRootView} from './interfaces/type_checks';
|
||||||
import {CHILD_HEAD, CLEANUP, FLAGS, HOST, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
import {CHILD_HEAD, CLEANUP, DECLARATION_LCONTAINER, FLAGS, HOST, HookData, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, T_HOST, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {renderStringify} from './util/misc_utils';
|
import {renderStringify} from './util/misc_utils';
|
||||||
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
|
import {findComponentView, getLViewParent} from './util/view_traversal_utils';
|
||||||
import {getNativeByTNode, getNativeByTNodeOrNull, unwrapRNode, viewAttachedToContainer} from './util/view_utils';
|
import {getNativeByTNode, getNativeByTNodeOrNull, unwrapRNode} from './util/view_utils';
|
||||||
|
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||||
|
|
||||||
|
@ -217,15 +217,46 @@ export function insertView(lView: LView, lContainer: LContainer, index: number)
|
||||||
|
|
||||||
lView[PARENT] = lContainer;
|
lView[PARENT] = lContainer;
|
||||||
|
|
||||||
// Notify query that a new view has been added
|
// track views where declaration and insertion points are different
|
||||||
if (lView[QUERIES]) {
|
const declarationLContainer = lView[DECLARATION_LCONTAINER];
|
||||||
lView[QUERIES] !.insertView(index);
|
if (declarationLContainer !== null && lContainer !== declarationLContainer) {
|
||||||
|
trackMovedView(declarationLContainer, lView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify query that a new view has been added
|
||||||
|
const lQueries = lView[QUERIES];
|
||||||
|
if (lQueries !== null) {
|
||||||
|
lQueries.insertView(lView[TVIEW]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the attached flag
|
// Sets the attached flag
|
||||||
lView[FLAGS] |= LViewFlags.Attached;
|
lView[FLAGS] |= LViewFlags.Attached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track views created from the declaration container (TemplateRef) and inserted into a
|
||||||
|
* different LContainer.
|
||||||
|
*/
|
||||||
|
function trackMovedView(declarationContainer: LContainer, lView: LView) {
|
||||||
|
ngDevMode && assertLContainer(declarationContainer);
|
||||||
|
const declaredViews = declarationContainer[MOVED_VIEWS];
|
||||||
|
if (declaredViews === null) {
|
||||||
|
declarationContainer[MOVED_VIEWS] = [lView];
|
||||||
|
} else {
|
||||||
|
declaredViews.push(lView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detachMovedView(declarationContainer: LContainer, lView: LView) {
|
||||||
|
ngDevMode && assertLContainer(declarationContainer);
|
||||||
|
ngDevMode && assertDefined(
|
||||||
|
declarationContainer[MOVED_VIEWS],
|
||||||
|
'A projected view should belong to a non-empty projected views collection');
|
||||||
|
const projectedViews = declarationContainer[MOVED_VIEWS] !;
|
||||||
|
const declaredViewIndex = projectedViews.indexOf(lView);
|
||||||
|
projectedViews.splice(declaredViewIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detaches a view from a container.
|
* Detaches a view from a container.
|
||||||
*
|
*
|
||||||
|
@ -241,17 +272,26 @@ export function detachView(lContainer: LContainer, removeIndex: number): LView|u
|
||||||
|
|
||||||
const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
|
const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
|
||||||
const viewToDetach = lContainer[indexInContainer];
|
const viewToDetach = lContainer[indexInContainer];
|
||||||
|
|
||||||
if (viewToDetach) {
|
if (viewToDetach) {
|
||||||
|
const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
|
||||||
|
if (declarationLContainer !== null && declarationLContainer !== lContainer) {
|
||||||
|
detachMovedView(declarationLContainer, viewToDetach);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (removeIndex > 0) {
|
if (removeIndex > 0) {
|
||||||
lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT] as LView;
|
lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT] as LView;
|
||||||
}
|
}
|
||||||
lContainer.splice(CONTAINER_HEADER_OFFSET + removeIndex, 1);
|
const removedLView = lContainer.splice(CONTAINER_HEADER_OFFSET + removeIndex, 1)[0];
|
||||||
addRemoveViewFromContainer(viewToDetach, false);
|
addRemoveViewFromContainer(viewToDetach, false);
|
||||||
|
|
||||||
if ((viewToDetach[FLAGS] & LViewFlags.Attached) &&
|
// notify query that a view has been removed
|
||||||
!(viewToDetach[FLAGS] & LViewFlags.Destroyed) && viewToDetach[QUERIES]) {
|
const lQueries = removedLView[QUERIES];
|
||||||
viewToDetach[QUERIES] !.removeView();
|
if (lQueries !== null) {
|
||||||
|
lQueries.detachView(removedLView[TVIEW]);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewToDetach[PARENT] = null;
|
viewToDetach[PARENT] = null;
|
||||||
viewToDetach[NEXT] = null;
|
viewToDetach[NEXT] = null;
|
||||||
// Unsets the attached flag
|
// Unsets the attached flag
|
||||||
|
@ -342,9 +382,20 @@ function cleanUpView(view: LView | LContainer): void {
|
||||||
ngDevMode && ngDevMode.rendererDestroy++;
|
ngDevMode && ngDevMode.rendererDestroy++;
|
||||||
(view[RENDERER] as ProceduralRenderer3).destroy();
|
(view[RENDERER] as ProceduralRenderer3).destroy();
|
||||||
}
|
}
|
||||||
// For embedded views still attached to a container: remove query result from this view.
|
|
||||||
if (viewAttachedToContainer(view) && view[QUERIES]) {
|
const declarationContainer = view[DECLARATION_LCONTAINER];
|
||||||
view[QUERIES] !.removeView();
|
// we are dealing with an embedded view that is still inserted into a container
|
||||||
|
if (declarationContainer !== null && isLContainer(view[PARENT])) {
|
||||||
|
// and this is a projected view
|
||||||
|
if (declarationContainer !== view[PARENT]) {
|
||||||
|
detachMovedView(declarationContainer, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For embedded views still attached to a container: remove query result from this view.
|
||||||
|
const lQueries = view[QUERIES];
|
||||||
|
if (lQueries !== null) {
|
||||||
|
lQueries.detachView(view[TVIEW]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -878,16 +929,18 @@ function executeActionOnNode(
|
||||||
renderer: Renderer3, action: WalkTNodeTreeAction, lView: LView, tNode: TNode,
|
renderer: Renderer3, action: WalkTNodeTreeAction, lView: LView, tNode: TNode,
|
||||||
renderParent: RElement | null, beforeNode: RNode | null | undefined): void {
|
renderParent: RElement | null, beforeNode: RNode | null | undefined): void {
|
||||||
const nodeType = tNode.type;
|
const nodeType = tNode.type;
|
||||||
if (nodeType === TNodeType.ElementContainer || nodeType === TNodeType.IcuContainer) {
|
if (!(tNode.flags & TNodeFlags.isDetached)) {
|
||||||
executeActionOnElementContainerOrIcuContainer(
|
if (nodeType === TNodeType.ElementContainer || nodeType === TNodeType.IcuContainer) {
|
||||||
renderer, action, lView, tNode as TElementContainerNode | TIcuContainerNode, renderParent,
|
executeActionOnElementContainerOrIcuContainer(
|
||||||
beforeNode);
|
renderer, action, lView, tNode as TElementContainerNode | TIcuContainerNode, renderParent,
|
||||||
} else if (nodeType === TNodeType.Projection) {
|
beforeNode);
|
||||||
executeActionOnProjection(
|
} else if (nodeType === TNodeType.Projection) {
|
||||||
renderer, action, lView, tNode as TProjectionNode, renderParent, beforeNode);
|
executeActionOnProjection(
|
||||||
} else {
|
renderer, action, lView, tNode as TProjectionNode, renderParent, beforeNode);
|
||||||
ngDevMode && assertNodeOfPossibleTypes(tNode, TNodeType.Element, TNodeType.Container);
|
} else {
|
||||||
executeActionOnElementOrContainer(
|
ngDevMode && assertNodeOfPossibleTypes(tNode, TNodeType.Element, TNodeType.Container);
|
||||||
action, renderer, renderParent, lView[tNode.index], beforeNode);
|
executeActionOnElementOrContainer(
|
||||||
|
action, renderer, renderParent, lView[tNode.index], beforeNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,195 +13,240 @@ import {Type} from '../interface/type';
|
||||||
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
|
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
|
||||||
import {QueryList} from '../linker/query_list';
|
import {QueryList} from '../linker/query_list';
|
||||||
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
|
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
|
||||||
import {assertDataInRange, assertDefined, assertEqual} from '../util/assert';
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
||||||
|
import {assertDataInRange, assertDefined, throwError} from '../util/assert';
|
||||||
|
import {stringify} from '../util/stringify';
|
||||||
|
|
||||||
import {assertPreviousIsParent} from './assert';
|
import {assertFirstTemplatePass, assertLContainer} from './assert';
|
||||||
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
||||||
import {NG_ELEMENT_ID} from './fields';
|
|
||||||
import {store} from './instructions/all';
|
|
||||||
import {storeCleanupWithContext} from './instructions/shared';
|
import {storeCleanupWithContext} from './instructions/shared';
|
||||||
|
import {CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from './interfaces/container';
|
||||||
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
||||||
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
||||||
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
||||||
import {isContentQueryHost} from './interfaces/type_checks';
|
import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view';
|
||||||
import {CONTENT_QUERIES, HEADER_OFFSET, LView, QUERIES, TVIEW, TView} from './interfaces/view';
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {getCurrentQueryIndex, getIsParent, getLView, getPreviousOrParentTNode, isCreationMode, setCurrentQueryIndex} from './state';
|
import {getCurrentQueryIndex, getLView, getPreviousOrParentTNode, isCreationMode, setCurrentQueryIndex} from './state';
|
||||||
import {loadInternal} from './util/view_utils';
|
import {createContainerRef, createElementRef, createTemplateRef} from './view_engine_compatibility';
|
||||||
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
|
|
||||||
|
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
||||||
|
|
||||||
/**
|
class LQuery_<T> implements LQuery<T> {
|
||||||
* A predicate which determines if a given element/directive should be included in the query
|
matches: (T|null)[]|null = null;
|
||||||
* results.
|
constructor(public queryList: QueryList<T>) {}
|
||||||
*/
|
clone(): LQuery<T> { return new LQuery_(this.queryList); }
|
||||||
export interface QueryPredicate<T> {
|
setDirty(): void { this.queryList.setDirty(); }
|
||||||
/**
|
|
||||||
* If looking for directives then it contains the directive type.
|
|
||||||
*/
|
|
||||||
type: Type<T>|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If selector then contains local names to query for.
|
|
||||||
*/
|
|
||||||
selector: string[]|null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates which token should be read from DI for this query.
|
|
||||||
*/
|
|
||||||
read: Type<T>|null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
class LQueries_ implements LQueries {
|
||||||
* An object representing a query, which is a combination of:
|
constructor(public queries: LQuery<any>[] = []) {}
|
||||||
* - query predicate to determines if a given element/directive should be included in the query
|
|
||||||
* - values collected based on a predicate
|
|
||||||
* - `QueryList` to which collected values should be reported
|
|
||||||
*/
|
|
||||||
class LQuery<T> {
|
|
||||||
constructor(
|
|
||||||
/**
|
|
||||||
* Next query. Used when queries are stored as a linked list in `LQueries`.
|
|
||||||
*/
|
|
||||||
public next: LQuery<any>|null,
|
|
||||||
|
|
||||||
/**
|
createEmbeddedView(tView: TView): LQueries|null {
|
||||||
* Destination to which the value should be added.
|
const tQueries = tView.queries;
|
||||||
*/
|
if (tQueries !== null) {
|
||||||
public list: QueryList<T>,
|
const noOfInheritedQueries =
|
||||||
|
tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
|
||||||
|
const viewLQueries: LQuery<any>[] = new Array(noOfInheritedQueries);
|
||||||
|
|
||||||
/**
|
// An embedded view has queries propagated from a declaration view at the beginning of the
|
||||||
* A predicate which determines if a given element/directive should be included in the query
|
// TQueries collection and up until a first content query declared in the embedded view. Only
|
||||||
* results.
|
// propagated LQueries are created at this point (LQuery corresponding to declared content
|
||||||
*/
|
// queries will be instantiated from the content query instructions for each directive).
|
||||||
public predicate: QueryPredicate<T>,
|
for (let i = 0; i < noOfInheritedQueries; i++) {
|
||||||
|
const tQuery = tQueries.getByIndex(i);
|
||||||
|
const parentLQuery = this.queries[tQuery.indexInDeclarationView];
|
||||||
|
viewLQueries[i] = parentLQuery.clone();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
return new LQueries_(viewLQueries);
|
||||||
* Values which have been located.
|
}
|
||||||
* This is what builds up the `QueryList._valuesTree`.
|
|
||||||
*/
|
|
||||||
public values: any[],
|
|
||||||
|
|
||||||
/**
|
return null;
|
||||||
* A pointer to an array that stores collected values from views. This is necessary so we
|
}
|
||||||
* know a container into which to insert nodes collected from views.
|
|
||||||
*/
|
insertView(tView: TView): void { this.dirtyQueriesWithMatches(tView); }
|
||||||
public containerValues: any[]|null) {}
|
|
||||||
|
detachView(tView: TView): void { this.dirtyQueriesWithMatches(tView); }
|
||||||
|
|
||||||
|
private dirtyQueriesWithMatches(tView: TView) {
|
||||||
|
for (let i = 0; i < this.queries.length; i++) {
|
||||||
|
if (getTQuery(tView, i).matches !== null) {
|
||||||
|
this.queries[i].setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LQueries_ implements LQueries {
|
class TQueryMetadata_ implements TQueryMetadata {
|
||||||
constructor(
|
constructor(
|
||||||
public parent: LQueries_|null, private shallow: LQuery<any>|null,
|
public predicate: Type<any>|string[], public descendants: boolean, public read: any,
|
||||||
private deep: LQuery<any>|null, public nodeIndex: number = -1) {}
|
public isStatic: boolean) {}
|
||||||
|
}
|
||||||
|
|
||||||
track<T>(queryList: QueryList<T>, predicate: Type<T>|string[], descend?: boolean, read?: Type<T>):
|
class TQueries_ implements TQueries {
|
||||||
void {
|
constructor(private queries: TQuery[] = []) {}
|
||||||
if (descend) {
|
|
||||||
this.deep = createLQuery(this.deep, queryList, predicate, read != null ? read : null);
|
elementStart(tView: TView, tNode: TNode): void {
|
||||||
|
ngDevMode && assertFirstTemplatePass(
|
||||||
|
tView, 'Queries should collect results on the first template pass only');
|
||||||
|
for (let query of this.queries) {
|
||||||
|
query.elementStart(tView, tNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elementEnd(tNode: TNode): void {
|
||||||
|
for (let query of this.queries) {
|
||||||
|
query.elementEnd(tNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
embeddedTView(tNode: TNode): TQueries|null {
|
||||||
|
let queriesForTemplateRef: TQuery[]|null = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
const childQueryIndex = queriesForTemplateRef !== null ? queriesForTemplateRef.length : 0;
|
||||||
|
const tqueryClone = this.getByIndex(i).embeddedTView(tNode, childQueryIndex);
|
||||||
|
|
||||||
|
if (tqueryClone) {
|
||||||
|
tqueryClone.indexInDeclarationView = i;
|
||||||
|
if (queriesForTemplateRef !== null) {
|
||||||
|
queriesForTemplateRef.push(tqueryClone);
|
||||||
|
} else {
|
||||||
|
queriesForTemplateRef = [tqueryClone];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
template(tView: TView, tNode: TNode): void {
|
||||||
|
ngDevMode && assertFirstTemplatePass(
|
||||||
|
tView, 'Queries should collect results on the first template pass only');
|
||||||
|
for (let query of this.queries) {
|
||||||
|
query.template(tView, tNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getByIndex(index: number): TQuery {
|
||||||
|
ngDevMode && assertDataInRange(this.queries, index);
|
||||||
|
return this.queries[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
get length(): number { return this.queries.length; }
|
||||||
|
|
||||||
|
track(tquery: TQuery): void { this.queries.push(tquery); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TQuery_ implements TQuery {
|
||||||
|
matches: number[]|null = null;
|
||||||
|
indexInDeclarationView = -1;
|
||||||
|
crossesNgTemplate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node index on which a query was declared (-1 for view queries and ones inherited from the
|
||||||
|
* declaration template). We use this index (alongside with _appliesToNextNode flag) to know
|
||||||
|
* when to apply content queries to elements in a template.
|
||||||
|
*/
|
||||||
|
private _declarationNodeIndex: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag indicating if a given query still applies to nodes it is crossing. We use this flag
|
||||||
|
* (alongside with _declarationNodeIndex) to know when to stop applying content queries to
|
||||||
|
* elements in a template.
|
||||||
|
*/
|
||||||
|
private _appliesToNextNode = true;
|
||||||
|
|
||||||
|
constructor(public metadata: TQueryMetadata, nodeIndex: number = -1) {
|
||||||
|
this._declarationNodeIndex = nodeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
elementStart(tView: TView, tNode: TNode): void {
|
||||||
|
if (this.isApplyingToNode(tNode)) {
|
||||||
|
this.matchTNode(tView, tNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elementEnd(tNode: TNode): void {
|
||||||
|
if (this._declarationNodeIndex === tNode.index) {
|
||||||
|
this._appliesToNextNode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template(tView: TView, tNode: TNode): void { this.elementStart(tView, tNode); }
|
||||||
|
|
||||||
|
embeddedTView(tNode: TNode, childQueryIndex: number): TQuery|null {
|
||||||
|
if (this.isApplyingToNode(tNode)) {
|
||||||
|
this.crossesNgTemplate = true;
|
||||||
|
// A marker indicating a `<ng-template>` element (a placeholder for query results from
|
||||||
|
// embedded views created based on this `<ng-template>`).
|
||||||
|
this.addMatch(-tNode.index, childQueryIndex);
|
||||||
|
return new TQuery_(this.metadata);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isApplyingToNode(tNode: TNode): boolean {
|
||||||
|
if (this._appliesToNextNode && this.metadata.descendants === false) {
|
||||||
|
return this._declarationNodeIndex === (tNode.parent ? tNode.parent.index : -1);
|
||||||
|
}
|
||||||
|
return this._appliesToNextNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private matchTNode(tView: TView, tNode: TNode): void {
|
||||||
|
if (Array.isArray(this.metadata.predicate)) {
|
||||||
|
this.matchTNodeByLocalNames(tView, tNode, this.metadata.predicate as string[]);
|
||||||
} else {
|
} else {
|
||||||
this.shallow = createLQuery(this.shallow, queryList, predicate, read != null ? read : null);
|
const typePredicate = this.metadata.predicate as any;
|
||||||
|
if (typePredicate === ViewEngine_TemplateRef) {
|
||||||
|
this.matchTNodeByTemplateRef(tView, tNode);
|
||||||
|
} else {
|
||||||
|
this.matchTNodeByType(tView, tNode, typePredicate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clone(tNode: TNode): LQueries {
|
private matchTNodeByLocalNames(tView: TView, tNode: TNode, localNames: string[]): void {
|
||||||
return this.shallow !== null || isContentQueryHost(tNode) ?
|
for (let i = 0; i < localNames.length; i++) {
|
||||||
new LQueries_(this, null, this.deep, tNode.index) :
|
this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, localNames[i]));
|
||||||
this;
|
|
||||||
}
|
|
||||||
|
|
||||||
container(): LQueries|null {
|
|
||||||
const shallowResults = copyQueriesToContainer(this.shallow);
|
|
||||||
const deepResults = copyQueriesToContainer(this.deep);
|
|
||||||
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
createView(): LQueries|null {
|
|
||||||
const shallowResults = copyQueriesToView(this.shallow);
|
|
||||||
const deepResults = copyQueriesToView(this.deep);
|
|
||||||
|
|
||||||
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
insertView(index: number): void {
|
|
||||||
insertView(index, this.shallow);
|
|
||||||
insertView(index, this.deep);
|
|
||||||
}
|
|
||||||
|
|
||||||
addNode(tNode: TElementNode|TContainerNode|TElementContainerNode): void {
|
|
||||||
add(this.deep, tNode, false);
|
|
||||||
add(this.shallow, tNode, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertNodeBeforeViews(tNode: TElementNode|TContainerNode|TElementContainerNode): void {
|
|
||||||
add(this.deep, tNode, true);
|
|
||||||
add(this.shallow, tNode, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeView(): void {
|
|
||||||
removeView(this.shallow);
|
|
||||||
removeView(this.deep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyQueriesToContainer(query: LQuery<any>| null): LQuery<any>|null {
|
|
||||||
let result: LQuery<any>|null = null;
|
|
||||||
|
|
||||||
while (query) {
|
|
||||||
const containerValues: any[] = []; // prepare room for views
|
|
||||||
query.values.push(containerValues);
|
|
||||||
result = new LQuery<any>(result, query.list, query.predicate, containerValues, null);
|
|
||||||
query = query.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyQueriesToView(query: LQuery<any>| null): LQuery<any>|null {
|
|
||||||
let result: LQuery<any>|null = null;
|
|
||||||
|
|
||||||
while (query) {
|
|
||||||
result = new LQuery<any>(result, query.list, query.predicate, [], query.values);
|
|
||||||
query = query.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertView(index: number, query: LQuery<any>| null) {
|
|
||||||
while (query) {
|
|
||||||
ngDevMode && assertViewQueryhasPointerToDeclarationContainer(query);
|
|
||||||
query.containerValues !.splice(index, 0, query.values);
|
|
||||||
|
|
||||||
// mark a query as dirty only when inserted view had matching modes
|
|
||||||
if (query.values.length) {
|
|
||||||
query.list.setDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query.next;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function removeView(query: LQuery<any>| null) {
|
private matchTNodeByTemplateRef(tView: TView, tNode: TNode): void {
|
||||||
while (query) {
|
this.matchTNodeWithReadOption(tView, tNode, tNode.type === TNodeType.Container ? -1 : null);
|
||||||
ngDevMode && assertViewQueryhasPointerToDeclarationContainer(query);
|
}
|
||||||
|
|
||||||
const containerValues = query.containerValues !;
|
private matchTNodeByType(tView: TView, tNode: TNode, typePredicate: Type<any>): void {
|
||||||
const viewValuesIdx = containerValues.indexOf(query.values);
|
this.matchTNodeWithReadOption(
|
||||||
const removed = containerValues.splice(viewValuesIdx, 1);
|
tView, tNode, locateDirectiveOrProvider(tNode, tView, typePredicate, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
// mark a query as dirty only when removed view had matching modes
|
private matchTNodeWithReadOption(tView: TView, tNode: TNode, nodeMatchIdx: number|null): void {
|
||||||
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
if (nodeMatchIdx !== null) {
|
||||||
if (removed[0].length) {
|
const read = this.metadata.read;
|
||||||
query.list.setDirty();
|
if (read !== null) {
|
||||||
|
if (read === ViewEngine_ElementRef || read === ViewContainerRef ||
|
||||||
|
read === ViewEngine_TemplateRef && tNode.type === TNodeType.Container) {
|
||||||
|
this.addMatch(tNode.index, -2);
|
||||||
|
} else {
|
||||||
|
const directiveOrProviderIdx =
|
||||||
|
locateDirectiveOrProvider(tNode, tView, read, false, false);
|
||||||
|
if (directiveOrProviderIdx !== null) {
|
||||||
|
this.addMatch(tNode.index, directiveOrProviderIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.addMatch(tNode.index, nodeMatchIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query = query.next;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function assertViewQueryhasPointerToDeclarationContainer(query: LQuery<any>) {
|
private addMatch(tNodeIdx: number, matchIdx: number) {
|
||||||
assertDefined(query.containerValues, 'View queries need to have a pointer to container values.');
|
if (this.matches === null) {
|
||||||
|
this.matches = [tNodeIdx, matchIdx];
|
||||||
|
} else {
|
||||||
|
this.matches.push(tNodeIdx, matchIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,7 +259,7 @@ function assertViewQueryhasPointerToDeclarationContainer(query: LQuery<any>) {
|
||||||
*/
|
*/
|
||||||
function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
||||||
const localNames = tNode.localNames;
|
const localNames = tNode.localNames;
|
||||||
if (localNames) {
|
if (localNames !== null) {
|
||||||
for (let i = 0; i < localNames.length; i += 2) {
|
for (let i = 0; i < localNames.length; i += 2) {
|
||||||
if (localNames[i] === selector) {
|
if (localNames[i] === selector) {
|
||||||
return localNames[i + 1] as number;
|
return localNames[i + 1] as number;
|
||||||
|
@ -225,148 +270,127 @@ function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: "read" should be an AbstractType (FW-486)
|
function createResultByTNodeType(tNode: TNode, currentView: LView): any {
|
||||||
function queryByReadToken(read: any, tNode: TNode, currentView: LView): any {
|
|
||||||
const factoryFn = (read as any)[NG_ELEMENT_ID];
|
|
||||||
if (typeof factoryFn === 'function') {
|
|
||||||
return factoryFn();
|
|
||||||
} else {
|
|
||||||
const tView = currentView[TVIEW];
|
|
||||||
const matchingIdx = locateDirectiveOrProvider(tNode, tView, read as Type<any>, false, false);
|
|
||||||
if (matchingIdx !== null) {
|
|
||||||
return getNodeInjectable(tView.data, currentView, matchingIdx, tNode as TElementNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryByTNodeType(tNode: TNode, currentView: LView): any {
|
|
||||||
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
||||||
return createElementRef(ViewEngine_ElementRef, tNode, currentView);
|
return createElementRef(ViewEngine_ElementRef, tNode, currentView);
|
||||||
}
|
} else if (tNode.type === TNodeType.Container) {
|
||||||
if (tNode.type === TNodeType.Container) {
|
|
||||||
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
|
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryByTemplateRef(
|
|
||||||
templateRefToken: ViewEngine_TemplateRef<any>, tNode: TNode, currentView: LView,
|
function createResultForNode(lView: LView, tNode: TNode, matchingIdx: number, read: any): any {
|
||||||
read: any): any {
|
if (matchingIdx === -1) {
|
||||||
const templateRefResult = (templateRefToken as any)[NG_ELEMENT_ID]();
|
// if read token and / or strategy is not specified, detect it using appropriate tNode type
|
||||||
if (read) {
|
return createResultByTNodeType(tNode, lView);
|
||||||
return templateRefResult ? queryByReadToken(read, tNode, currentView) : null;
|
} else if (matchingIdx === -2) {
|
||||||
|
// read a special token from a node injector
|
||||||
|
return createSpecialToken(lView, tNode, read);
|
||||||
|
} else {
|
||||||
|
// read a token
|
||||||
|
return getNodeInjectable(lView[TVIEW].data, lView, matchingIdx, tNode as TElementNode);
|
||||||
}
|
}
|
||||||
return templateRefResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function queryRead(tNode: TNode, currentView: LView, read: any, matchingIdx: number): any {
|
function createSpecialToken(lView: LView, tNode: TNode, read: any): any {
|
||||||
if (read) {
|
if (read === ViewEngine_ElementRef) {
|
||||||
return queryByReadToken(read, tNode, currentView);
|
return createElementRef(ViewEngine_ElementRef, tNode, lView);
|
||||||
|
} else if (read === ViewEngine_TemplateRef) {
|
||||||
|
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, lView);
|
||||||
|
} else if (read === ViewContainerRef) {
|
||||||
|
ngDevMode && assertNodeOfPossibleTypes(
|
||||||
|
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
|
||||||
|
return createContainerRef(
|
||||||
|
ViewContainerRef, ViewEngine_ElementRef,
|
||||||
|
tNode as TElementNode | TContainerNode | TElementContainerNode, lView);
|
||||||
|
} else {
|
||||||
|
ngDevMode &&
|
||||||
|
throwError(
|
||||||
|
`Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`);
|
||||||
}
|
}
|
||||||
if (matchingIdx > -1) {
|
|
||||||
return getNodeInjectable(
|
|
||||||
currentView[TVIEW].data, currentView, matchingIdx, tNode as TElementNode);
|
|
||||||
}
|
|
||||||
// if read token and / or strategy is not specified,
|
|
||||||
// detect it using appropriate tNode type
|
|
||||||
return queryByTNodeType(tNode, currentView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add query matches for a given node.
|
* A helper function that creates query results for a given view. This function is meant to do the
|
||||||
*
|
* processing once and only once for a given view instance (a set of results for a given view
|
||||||
* @param query The first query in the linked list
|
* doesn't change).
|
||||||
* @param tNode The TNode to match against queries
|
|
||||||
* @param insertBeforeContainer Whether or not we should add matches before the last
|
|
||||||
* container array. This mode is necessary if the query container had to be created
|
|
||||||
* out of order (e.g. a view was created in a constructor)
|
|
||||||
*/
|
*/
|
||||||
function add(
|
function materializeViewResults<T>(lView: LView, tQuery: TQuery, queryIndex: number): (T | null)[] {
|
||||||
query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode,
|
const lQuery = lView[QUERIES] !.queries ![queryIndex];
|
||||||
insertBeforeContainer: boolean) {
|
if (lQuery.matches === null) {
|
||||||
const lView = getLView();
|
const tViewData = lView[TVIEW].data;
|
||||||
const tView = lView[TVIEW];
|
const tQueryMatches = tQuery.matches !;
|
||||||
|
const result: T|null[] = new Array(tQueryMatches.length / 2);
|
||||||
while (query) {
|
for (let i = 0; i < tQueryMatches.length; i += 2) {
|
||||||
const predicate = query.predicate;
|
const matchedNodeIdx = tQueryMatches[i];
|
||||||
const type = predicate.type as any;
|
if (matchedNodeIdx < 0) {
|
||||||
if (type) {
|
// we at the <ng-template> marker which might have results in views created based on this
|
||||||
let result = null;
|
// <ng-template> - those results will be in separate views though, so here we just leave
|
||||||
if (type === ViewEngine_TemplateRef) {
|
// null as a placeholder
|
||||||
result = queryByTemplateRef(type, tNode, lView, predicate.read);
|
result[i / 2] = null;
|
||||||
} else {
|
} else {
|
||||||
const matchingIdx = locateDirectiveOrProvider(tNode, tView, type, false, false);
|
ngDevMode && assertDataInRange(tViewData, matchedNodeIdx);
|
||||||
if (matchingIdx !== null) {
|
const tNode = tViewData[matchedNodeIdx] as TNode;
|
||||||
result = queryRead(tNode, lView, predicate.read, matchingIdx);
|
result[i / 2] =
|
||||||
|
createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lQuery.matches = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lQuery.matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function that collects (already materialized) query results from a tree of views,
|
||||||
|
* starting with a provided LView.
|
||||||
|
*/
|
||||||
|
function collectQueryResults<T>(
|
||||||
|
lView: LView, tQuery: TQuery, queryIndex: number, result: T[]): T[] {
|
||||||
|
ngDevMode &&
|
||||||
|
assertDefined(
|
||||||
|
tQuery.matches, 'Query results can only be collected for queries with existing matches.');
|
||||||
|
|
||||||
|
const tQueryMatches = tQuery.matches !;
|
||||||
|
const lViewResults = materializeViewResults<T>(lView, tQuery, queryIndex);
|
||||||
|
|
||||||
|
for (let i = 0; i < tQueryMatches.length; i += 2) {
|
||||||
|
const tNodeIdx = tQueryMatches[i];
|
||||||
|
if (tNodeIdx > 0) {
|
||||||
|
const viewResult = lViewResults[i / 2];
|
||||||
|
ngDevMode && assertDefined(viewResult, 'materialized query result should be defined');
|
||||||
|
result.push(viewResult as T);
|
||||||
|
} else {
|
||||||
|
const childQueryIndex = tQueryMatches[i + 1];
|
||||||
|
|
||||||
|
const declarationLContainer = lView[-tNodeIdx] as LContainer;
|
||||||
|
ngDevMode && assertLContainer(declarationLContainer);
|
||||||
|
|
||||||
|
// collect matches for views inserted in this container
|
||||||
|
for (let i = CONTAINER_HEADER_OFFSET; i < declarationLContainer.length; i++) {
|
||||||
|
const embeddedLView = declarationLContainer[i];
|
||||||
|
if (embeddedLView[DECLARATION_LCONTAINER] === embeddedLView[PARENT]) {
|
||||||
|
const tquery = getTQuery(embeddedLView[TVIEW], childQueryIndex);
|
||||||
|
if (tquery.matches !== null) {
|
||||||
|
collectQueryResults(embeddedLView, tquery, childQueryIndex, result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result !== null) {
|
|
||||||
addMatch(query, result, insertBeforeContainer);
|
// collect matches for views created from this declaration container and inserted into
|
||||||
}
|
// different containers
|
||||||
} else {
|
if (declarationLContainer[MOVED_VIEWS] !== null) {
|
||||||
const selector = predicate.selector !;
|
for (let embeddedLView of declarationLContainer[MOVED_VIEWS] !) {
|
||||||
for (let i = 0; i < selector.length; i++) {
|
const tquery = getTQuery(embeddedLView[TVIEW], childQueryIndex);
|
||||||
const matchingIdx = getIdxOfMatchingSelector(tNode, selector[i]);
|
if (tquery.matches !== null) {
|
||||||
if (matchingIdx !== null) {
|
collectQueryResults(embeddedLView, tquery, childQueryIndex, result);
|
||||||
const result = queryRead(tNode, lView, predicate.read, matchingIdx);
|
|
||||||
if (result !== null) {
|
|
||||||
addMatch(query, result, insertBeforeContainer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
query = query.next;
|
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
|
|
||||||
function addMatch(query: LQuery<any>, matchingValue: any, insertBeforeViewMatches: boolean): void {
|
|
||||||
// Views created in constructors may have their container values created too early. In this case,
|
|
||||||
// ensure template node results are unshifted before container results. Otherwise, results inside
|
|
||||||
// embedded views will appear before results on parent template nodes when flattened.
|
|
||||||
insertBeforeViewMatches ? query.values.unshift(matchingValue) : query.values.push(matchingValue);
|
|
||||||
query.list.setDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPredicate<T>(predicate: Type<T>| string[], read: Type<T>| null): QueryPredicate<T> {
|
|
||||||
const isArray = Array.isArray(predicate);
|
|
||||||
return {
|
|
||||||
type: isArray ? null : predicate as Type<T>,
|
|
||||||
selector: isArray ? predicate as string[] : null,
|
|
||||||
read: read
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLQuery<T>(
|
|
||||||
previous: LQuery<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
|
||||||
read: Type<T>| null): LQuery<T> {
|
|
||||||
return new LQuery(
|
|
||||||
previous, queryList, createPredicate(predicate, read),
|
|
||||||
(queryList as any as QueryList_<T>)._valuesTree, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueryList_<T> = QueryList<T>& {_valuesTree: any[], _static: boolean};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a QueryList and stores it in LView's collection of active queries (LQueries).
|
|
||||||
*
|
|
||||||
* @param predicate The type for which the query will search
|
|
||||||
* @param descend Whether or not to descend into children
|
|
||||||
* @param read What to save in the query
|
|
||||||
* @returns QueryList<T>
|
|
||||||
*/
|
|
||||||
function createQueryListInLView<T>(
|
|
||||||
// TODO: "read" should be an AbstractType (FW-486)
|
|
||||||
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, isStatic: boolean,
|
|
||||||
nodeIndex: number): QueryList<T> {
|
|
||||||
ngDevMode && assertPreviousIsParent(getIsParent());
|
|
||||||
const queryList = new QueryList<T>() as QueryList_<T>;
|
|
||||||
const queries = lView[QUERIES] || (lView[QUERIES] = new LQueries_(null, null, null, nodeIndex));
|
|
||||||
queryList._valuesTree = [];
|
|
||||||
queryList._static = isStatic;
|
|
||||||
queries.track(queryList, predicate, descend, read);
|
|
||||||
storeCleanupWithContext(lView, queryList, queryList.destroy);
|
|
||||||
return queryList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -379,15 +403,24 @@ function createQueryListInLView<T>(
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
||||||
const queryListImpl = (queryList as any as QueryList_<any>);
|
const lView = getLView();
|
||||||
const creationMode = isCreationMode();
|
const queryIndex = getCurrentQueryIndex();
|
||||||
|
|
||||||
// if creation mode and static or update mode and not static
|
setCurrentQueryIndex(queryIndex + 1);
|
||||||
if (queryList.dirty && creationMode === queryListImpl._static) {
|
|
||||||
queryList.reset(queryListImpl._valuesTree || []);
|
const tQuery = getTQuery(lView[TVIEW], queryIndex);
|
||||||
queryList.notifyOnChanges();
|
if (queryList.dirty && (isCreationMode() === tQuery.metadata.isStatic)) {
|
||||||
|
if (tQuery.matches === null) {
|
||||||
|
queryList.reset([]);
|
||||||
|
} else {
|
||||||
|
const result = tQuery.crossesNgTemplate ? collectQueryResults(lView, tQuery, queryIndex, []) :
|
||||||
|
materializeViewResults(lView, tQuery, queryIndex);
|
||||||
|
queryList.reset(result);
|
||||||
|
queryList.notifyOnChanges();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,12 +434,8 @@ export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵstaticViewQuery<T>(
|
export function ɵɵstaticViewQuery<T>(
|
||||||
// TODO(FW-486): "read" should be an AbstractType
|
|
||||||
predicate: Type<any>| string[], descend: boolean, read: any): void {
|
predicate: Type<any>| string[], descend: boolean, read: any): void {
|
||||||
const lView = getLView();
|
viewQueryInternal(getLView(), predicate, descend, read, true);
|
||||||
const tView = lView[TVIEW];
|
|
||||||
viewQueryInternal(lView, tView, predicate, descend, read, true);
|
|
||||||
tView.staticViewQueries = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -415,41 +444,33 @@ export function ɵɵstaticViewQuery<T>(
|
||||||
* @param predicate The type for which the query will search
|
* @param predicate The type for which the query will search
|
||||||
* @param descend Whether or not to descend into children
|
* @param descend Whether or not to descend into children
|
||||||
* @param read What to save in the query
|
* @param read What to save in the query
|
||||||
* @returns QueryList<T>
|
|
||||||
*
|
*
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵviewQuery<T>(
|
export function ɵɵviewQuery<T>(predicate: Type<any>| string[], descend: boolean, read: any): void {
|
||||||
// TODO(FW-486): "read" should be an AbstractType
|
viewQueryInternal(getLView(), predicate, descend, read, false);
|
||||||
predicate: Type<any>| string[], descend: boolean, read: any): QueryList<T> {
|
|
||||||
const lView = getLView();
|
|
||||||
const tView = lView[TVIEW];
|
|
||||||
return viewQueryInternal(lView, tView, predicate, descend, read, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewQueryInternal<T>(
|
function viewQueryInternal<T>(
|
||||||
lView: LView, tView: TView, predicate: Type<any>| string[], descend: boolean, read: any,
|
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any,
|
||||||
isStatic: boolean): QueryList<T> {
|
isStatic: boolean): void {
|
||||||
|
const tView = lView[TVIEW];
|
||||||
if (tView.firstTemplatePass) {
|
if (tView.firstTemplatePass) {
|
||||||
tView.expandoStartIndex++;
|
createTQuery(tView, new TQueryMetadata_(predicate, descend, read, isStatic), -1);
|
||||||
|
if (isStatic) {
|
||||||
|
tView.staticViewQueries = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const index = getCurrentQueryIndex();
|
createLQuery<T>(lView);
|
||||||
const queryList: QueryList<T> =
|
|
||||||
createQueryListInLView<T>(lView, predicate, descend, read, isStatic, -1);
|
|
||||||
store(index - HEADER_OFFSET, queryList);
|
|
||||||
setCurrentQueryIndex(index + 1);
|
|
||||||
return queryList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads current View Query and moves the pointer/index to the next View Query in LView.
|
* Loads a QueryList corresponding to the current view query.
|
||||||
*
|
*
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵloadViewQuery<T>(): T {
|
export function ɵɵloadViewQuery<T>(): QueryList<T> {
|
||||||
const index = getCurrentQueryIndex();
|
return loadQueryInternal<T>(getLView(), getCurrentQueryIndex());
|
||||||
setCurrentQueryIndex(index + 1);
|
|
||||||
return loadInternal<T>(getLView(), index - HEADER_OFFSET);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -465,33 +486,9 @@ export function ɵɵloadViewQuery<T>(): T {
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵcontentQuery<T>(
|
export function ɵɵcontentQuery<T>(
|
||||||
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read: any): void {
|
||||||
// TODO(FW-486): "read" should be an AbstractType
|
contentQueryInternal(
|
||||||
read: any): QueryList<T> {
|
getLView(), predicate, descend, read, false, getPreviousOrParentTNode(), directiveIndex);
|
||||||
const lView = getLView();
|
|
||||||
const tView = lView[TVIEW];
|
|
||||||
const tNode = getPreviousOrParentTNode();
|
|
||||||
return contentQueryInternal(
|
|
||||||
lView, tView, directiveIndex, predicate, descend, read, false, tNode.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
function contentQueryInternal<T>(
|
|
||||||
lView: LView, tView: TView, directiveIndex: number, predicate: Type<any>| string[],
|
|
||||||
descend: boolean,
|
|
||||||
// TODO(FW-486): "read" should be an AbstractType
|
|
||||||
read: any, isStatic: boolean, nodeIndex: number): QueryList<T> {
|
|
||||||
const contentQuery: QueryList<T> =
|
|
||||||
createQueryListInLView<T>(lView, predicate, descend, read, isStatic, nodeIndex);
|
|
||||||
(lView[CONTENT_QUERIES] || (lView[CONTENT_QUERIES] = [])).push(contentQuery);
|
|
||||||
if (tView.firstTemplatePass) {
|
|
||||||
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
|
|
||||||
const lastSavedDirectiveIndex =
|
|
||||||
tView.contentQueries.length ? tView.contentQueries[tView.contentQueries.length - 1] : -1;
|
|
||||||
if (directiveIndex !== lastSavedDirectiveIndex) {
|
|
||||||
tViewContentQueries.push(directiveIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return contentQuery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -507,29 +504,65 @@ function contentQueryInternal<T>(
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵstaticContentQuery<T>(
|
export function ɵɵstaticContentQuery<T>(
|
||||||
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read: any): void {
|
||||||
// TODO(FW-486): "read" should be an AbstractType
|
contentQueryInternal(
|
||||||
read: any): void {
|
getLView(), predicate, descend, read, true, getPreviousOrParentTNode(), directiveIndex);
|
||||||
const lView = getLView();
|
}
|
||||||
|
|
||||||
|
function contentQueryInternal<T>(
|
||||||
|
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, isStatic: boolean,
|
||||||
|
tNode: TNode, directiveIndex: number): void {
|
||||||
const tView = lView[TVIEW];
|
const tView = lView[TVIEW];
|
||||||
const tNode = getPreviousOrParentTNode();
|
if (tView.firstTemplatePass) {
|
||||||
contentQueryInternal(lView, tView, directiveIndex, predicate, descend, read, true, tNode.index);
|
createTQuery(tView, new TQueryMetadata_(predicate, descend, read, isStatic), tNode.index);
|
||||||
tView.staticContentQueries = true;
|
saveContentQueryAndDirectiveIndex(tView, directiveIndex);
|
||||||
|
if (isStatic) {
|
||||||
|
tView.staticContentQueries = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createLQuery<T>(lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Loads a QueryList corresponding to the current content query.
|
||||||
*
|
*
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
export function ɵɵloadContentQuery<T>(): QueryList<T> {
|
export function ɵɵloadContentQuery<T>(): QueryList<T> {
|
||||||
const lView = getLView();
|
return loadQueryInternal<T>(getLView(), getCurrentQueryIndex());
|
||||||
ngDevMode &&
|
}
|
||||||
assertDefined(
|
|
||||||
lView[CONTENT_QUERIES], 'Content QueryList array should be defined if reading a query.');
|
function loadQueryInternal<T>(lView: LView, queryIndex: number): QueryList<T> {
|
||||||
|
ngDevMode &&
|
||||||
const index = getCurrentQueryIndex();
|
assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query');
|
||||||
ngDevMode && assertDataInRange(lView[CONTENT_QUERIES] !, index);
|
ngDevMode && assertDataInRange(lView[QUERIES] !.queries, queryIndex);
|
||||||
|
return lView[QUERIES] !.queries[queryIndex].queryList;
|
||||||
setCurrentQueryIndex(index + 1);
|
}
|
||||||
return lView[CONTENT_QUERIES] ![index];
|
|
||||||
|
function createLQuery<T>(lView: LView) {
|
||||||
|
const queryList = new QueryList<T>();
|
||||||
|
storeCleanupWithContext(lView, queryList, queryList.destroy);
|
||||||
|
|
||||||
|
if (lView[QUERIES] === null) lView[QUERIES] = new LQueries_();
|
||||||
|
lView[QUERIES] !.queries.push(new LQuery_(queryList));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTQuery(tView: TView, metadata: TQueryMetadata, nodeIndex: number): void {
|
||||||
|
if (tView.queries === null) tView.queries = new TQueries_();
|
||||||
|
tView.queries.track(new TQuery_(metadata, nodeIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveContentQueryAndDirectiveIndex(tView: TView, directiveIndex: number) {
|
||||||
|
const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
|
||||||
|
const lastSavedDirectiveIndex =
|
||||||
|
tView.contentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1;
|
||||||
|
if (directiveIndex !== lastSavedDirectiveIndex) {
|
||||||
|
tViewContentQueries.push(tView.queries !.length - 1, directiveIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTQuery(tView: TView, index: number): TQuery {
|
||||||
|
ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery');
|
||||||
|
return tView.queries !.getByIndex(index);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,16 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
|
||||||
import {Renderer2} from '../render/api';
|
import {Renderer2} from '../render/api';
|
||||||
import {assertDefined, assertGreaterThan, assertLessThan} from '../util/assert';
|
import {assertDefined, assertGreaterThan, assertLessThan} from '../util/assert';
|
||||||
|
|
||||||
|
import {assertLContainer} from './assert';
|
||||||
import {NodeInjector, getParentInjectorLocation} from './di';
|
import {NodeInjector, getParentInjectorLocation} from './di';
|
||||||
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions/shared';
|
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions/shared';
|
||||||
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container';
|
import {ACTIVE_INDEX, CONTAINER_HEADER_OFFSET, LContainer, VIEW_REFS} from './interfaces/container';
|
||||||
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
|
||||||
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
|
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
|
||||||
|
|
||||||
import {isComponent, isLContainer, isLView, isRootView} from './interfaces/type_checks';
|
import {isComponent, isLContainer, isLView, isRootView} from './interfaces/type_checks';
|
||||||
import {CONTEXT, LView, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
|
import {CONTEXT, DECLARATION_LCONTAINER, LView, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
|
||||||
|
|
||||||
import {assertNodeOfPossibleTypes} from './node_assert';
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
||||||
import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation';
|
import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation';
|
||||||
import {getParentInjectorTNode} from './node_util';
|
import {getParentInjectorTNode} from './node_util';
|
||||||
|
@ -66,9 +69,8 @@ export function createElementRef(
|
||||||
}
|
}
|
||||||
|
|
||||||
let R3TemplateRef: {
|
let R3TemplateRef: {
|
||||||
new (
|
new (_declarationParentView: LView, hostTNode: TContainerNode, elementRef: ViewEngine_ElementRef):
|
||||||
_declarationParentView: LView, elementRef: ViewEngine_ElementRef, _tView: TView,
|
ViewEngine_TemplateRef<any>
|
||||||
_hostLContainer: LContainer, _injectorIndex: number): ViewEngine_TemplateRef<any>
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,9 +90,9 @@ export function injectTemplateRef<T>(
|
||||||
*
|
*
|
||||||
* @param TemplateRefToken The TemplateRef type
|
* @param TemplateRefToken The TemplateRef type
|
||||||
* @param ElementRefToken The ElementRef type
|
* @param ElementRefToken The ElementRef type
|
||||||
* @param hostTNode The node that is requesting a TemplateRef
|
* @param hostTNode The node on which a TemplateRef is requested
|
||||||
* @param hostView The view to which the node belongs
|
* @param hostView The view to which the node belongs
|
||||||
* @returns The TemplateRef instance to use
|
* @returns The TemplateRef instance or null if we can't create a TemplateRef on a given node type
|
||||||
*/
|
*/
|
||||||
export function createTemplateRef<T>(
|
export function createTemplateRef<T>(
|
||||||
TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef,
|
TemplateRefToken: typeof ViewEngine_TemplateRef, ElementRefToken: typeof ViewEngine_ElementRef,
|
||||||
|
@ -99,27 +101,27 @@ export function createTemplateRef<T>(
|
||||||
// TODO: Fix class name, should be TemplateRef, but there appears to be a rollup bug
|
// TODO: Fix class name, should be TemplateRef, but there appears to be a rollup bug
|
||||||
R3TemplateRef = class TemplateRef_<T> extends TemplateRefToken<T> {
|
R3TemplateRef = class TemplateRef_<T> extends TemplateRefToken<T> {
|
||||||
constructor(
|
constructor(
|
||||||
private _declarationParentView: LView, readonly elementRef: ViewEngine_ElementRef,
|
private _declarationView: LView, private _declarationTContainer: TContainerNode,
|
||||||
private _tView: TView, private _hostLContainer: LContainer,
|
readonly elementRef: ViewEngine_ElementRef) {
|
||||||
private _injectorIndex: number) {
|
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
createEmbeddedView(context: T, container?: LContainer, index?: number):
|
createEmbeddedView(context: T): viewEngine_EmbeddedViewRef<T> {
|
||||||
viewEngine_EmbeddedViewRef<T> {
|
const embeddedTView = this._declarationTContainer.tViews as TView;
|
||||||
const currentQueries = this._declarationParentView[QUERIES];
|
|
||||||
// Query container may be missing if this view was created in a directive
|
|
||||||
// constructor. Create it now to avoid losing results in embedded views.
|
|
||||||
if (currentQueries && this._hostLContainer[QUERIES] == null) {
|
|
||||||
this._hostLContainer[QUERIES] = currentQueries !.container();
|
|
||||||
}
|
|
||||||
const lView = createEmbeddedViewAndNode(
|
const lView = createEmbeddedViewAndNode(
|
||||||
this._tView, context, this._declarationParentView, this._hostLContainer[QUERIES],
|
embeddedTView, context, this._declarationView,
|
||||||
this._injectorIndex);
|
this._declarationTContainer.injectorIndex);
|
||||||
if (container) {
|
|
||||||
insertView(lView, container, index !);
|
const declarationLContainer = this._declarationView[this._declarationTContainer.index];
|
||||||
|
ngDevMode && assertLContainer(declarationLContainer);
|
||||||
|
lView[DECLARATION_LCONTAINER] = declarationLContainer;
|
||||||
|
|
||||||
|
const declarationViewLQueries = this._declarationView[QUERIES];
|
||||||
|
if (declarationViewLQueries !== null) {
|
||||||
|
lView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
|
||||||
}
|
}
|
||||||
renderEmbeddedTemplate(lView, this._tView, context);
|
|
||||||
|
renderEmbeddedTemplate(lView, embeddedTView, context);
|
||||||
const viewRef = new ViewRef(lView, context, -1);
|
const viewRef = new ViewRef(lView, context, -1);
|
||||||
viewRef._tViewNode = lView[T_HOST] as TViewNode;
|
viewRef._tViewNode = lView[T_HOST] as TViewNode;
|
||||||
return viewRef;
|
return viewRef;
|
||||||
|
@ -128,11 +130,10 @@ export function createTemplateRef<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hostTNode.type === TNodeType.Container) {
|
if (hostTNode.type === TNodeType.Container) {
|
||||||
const hostContainer: LContainer = hostView[hostTNode.index];
|
|
||||||
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
|
||||||
return new R3TemplateRef(
|
return new R3TemplateRef(
|
||||||
hostView, createElementRef(ElementRefToken, hostTNode, hostView), hostTNode.tViews as TView,
|
hostView, hostTNode as TContainerNode,
|
||||||
hostContainer, hostTNode.injectorIndex);
|
createElementRef(ElementRefToken, hostTNode, hostView));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -218,12 +219,8 @@ export function createContainerRef(
|
||||||
|
|
||||||
createEmbeddedView<C>(templateRef: ViewEngine_TemplateRef<C>, context?: C, index?: number):
|
createEmbeddedView<C>(templateRef: ViewEngine_TemplateRef<C>, context?: C, index?: number):
|
||||||
viewEngine_EmbeddedViewRef<C> {
|
viewEngine_EmbeddedViewRef<C> {
|
||||||
this.allocateContainerIfNeeded();
|
const viewRef = templateRef.createEmbeddedView(context || <any>{});
|
||||||
const adjustedIdx = this._adjustIndex(index);
|
this.insert(viewRef, index);
|
||||||
const viewRef = (templateRef as any)
|
|
||||||
.createEmbeddedView(context || <any>{}, this._lContainer, adjustedIdx);
|
|
||||||
(viewRef as ViewRef<any>).attachToViewContainerRef(this);
|
|
||||||
this._lContainer[VIEW_REFS] !.splice(adjustedIdx, 0, viewRef);
|
|
||||||
return viewRef;
|
return viewRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
import {Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, forwardRef} from '@angular/core';
|
||||||
import {TestBed} from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
import {By} from '@angular/platform-browser';
|
import {By} from '@angular/platform-browser';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
@ -786,6 +786,56 @@ describe('query logic', () => {
|
||||||
expect(queryList.last.nativeElement.id).toBe('c');
|
expect(queryList.last.nativeElement.id).toBe('c');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support a mix of content queries from the declaration and embedded view', () => {
|
||||||
|
@Directive({selector: '[query-for-lots-of-content]'})
|
||||||
|
class QueryForLotsOfContent {
|
||||||
|
@ContentChildren('foo', {descendants: true}) foos1 !: QueryList<ElementRef>;
|
||||||
|
@ContentChildren('foo', {descendants: true}) foos2 !: QueryList<ElementRef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[query-for-content]'})
|
||||||
|
class QueryForContent {
|
||||||
|
@ContentChildren('foo') foos !: QueryList<ElementRef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-comp',
|
||||||
|
template: `
|
||||||
|
<div query-for-lots-of-content>
|
||||||
|
<ng-template ngFor let-item [ngForOf]="items">
|
||||||
|
<div query-for-content>
|
||||||
|
<span #foo></span>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestComponent {
|
||||||
|
items = [1, 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [TestComponent, QueryForContent, QueryForLotsOfContent]});
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const lotsOfContentEl = fixture.debugElement.query(By.directive(QueryForLotsOfContent));
|
||||||
|
const lotsOfContentInstance = lotsOfContentEl.injector.get(QueryForLotsOfContent);
|
||||||
|
|
||||||
|
const contentEl = fixture.debugElement.query(By.directive(QueryForContent));
|
||||||
|
const contentInstance = contentEl.injector.get(QueryForContent);
|
||||||
|
|
||||||
|
expect(lotsOfContentInstance.foos1.length).toBe(2);
|
||||||
|
expect(lotsOfContentInstance.foos2.length).toBe(2);
|
||||||
|
expect(contentInstance.foos.length).toBe(1);
|
||||||
|
|
||||||
|
fixture.componentInstance.items = [];
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(lotsOfContentInstance.foos1.length).toBe(0);
|
||||||
|
expect(lotsOfContentInstance.foos2.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-rrmmuf?file=src/app/app.component.ts
|
// https://stackblitz.com/edit/angular-rrmmuf?file=src/app/app.component.ts
|
||||||
it('should report results when different instances of TemplateRef are inserted into one ViewContainerRefs',
|
it('should report results when different instances of TemplateRef are inserted into one ViewContainerRefs',
|
||||||
() => {
|
() => {
|
||||||
|
@ -864,12 +914,11 @@ describe('query logic', () => {
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
|
// https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts
|
||||||
// https://stackblitz.com/edit/angular-xzwp6n
|
// https://stackblitz.com/edit/angular-xzwp6n
|
||||||
onlyInIvy('FW-1318: QueryList entries are ordered differently in Ivy.')
|
it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
|
||||||
.it('should report results when the same TemplateRef is inserted into different ViewContainerRefs',
|
() => {
|
||||||
() => {
|
@Component({
|
||||||
@Component({
|
selector: 'test-comp',
|
||||||
selector: 'test-comp',
|
template: `
|
||||||
template: `
|
|
||||||
<ng-template #tpl let-idx="idx" let-container_idx="container_idx">
|
<ng-template #tpl let-idx="idx" let-container_idx="container_idx">
|
||||||
<div #foo [id]="'foo_' + container_idx + '_' + idx"></div>
|
<div #foo [id]="'foo_' + container_idx + '_' + idx"></div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -877,44 +926,44 @@ describe('query logic', () => {
|
||||||
<ng-template vc #vi0="vc"></ng-template>
|
<ng-template vc #vi0="vc"></ng-template>
|
||||||
<ng-template vc #vi1="vc"></ng-template>
|
<ng-template vc #vi1="vc"></ng-template>
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
class TestComponent {
|
class TestComponent {
|
||||||
@ViewChild('tpl', {static: false}) tpl !: TemplateRef<any>;
|
@ViewChild('tpl', {static: false}) tpl !: TemplateRef<any>;
|
||||||
@ViewChild('vi0', {static: false}) vi0 !: ViewContainerManipulatorDirective;
|
@ViewChild('vi0', {static: false}) vi0 !: ViewContainerManipulatorDirective;
|
||||||
@ViewChild('vi1', {static: false}) vi1 !: ViewContainerManipulatorDirective;
|
@ViewChild('vi1', {static: false}) vi1 !: ViewContainerManipulatorDirective;
|
||||||
@ViewChildren('foo') query !: QueryList<any>;
|
@ViewChildren('foo') query !: QueryList<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule(
|
TestBed.configureTestingModule(
|
||||||
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
|
{declarations: [ViewContainerManipulatorDirective, TestComponent]});
|
||||||
const fixture = TestBed.createComponent(TestComponent);
|
const fixture = TestBed.createComponent(TestComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const queryList = fixture.componentInstance.query;
|
const queryList = fixture.componentInstance.query;
|
||||||
const {tpl, vi0, vi1} = fixture.componentInstance;
|
const {tpl, vi0, vi1} = fixture.componentInstance;
|
||||||
|
|
||||||
expect(queryList.length).toBe(0);
|
expect(queryList.length).toBe(0);
|
||||||
|
|
||||||
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
|
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
|
||||||
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
|
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(queryList.length).toBe(2);
|
expect(queryList.length).toBe(2);
|
||||||
let qListArr = queryList.toArray();
|
let qListArr = queryList.toArray();
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_0_0');
|
||||||
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_0_0');
|
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
||||||
|
|
||||||
vi0.remove();
|
vi0.remove();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(queryList.length).toBe(1);
|
expect(queryList.length).toBe(1);
|
||||||
qListArr = queryList.toArray();
|
qListArr = queryList.toArray();
|
||||||
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
expect(qListArr[0].nativeElement.getAttribute('id')).toBe('foo_1_0');
|
||||||
|
|
||||||
vi1.remove();
|
vi1.remove();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(queryList.length).toBe(0);
|
expect(queryList.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
|
// https://stackblitz.com/edit/angular-wpd6gv?file=src%2Fapp%2Fapp.component.ts
|
||||||
it('should report results from views inserted in a lifecycle hook', () => {
|
it('should report results from views inserted in a lifecycle hook', () => {
|
||||||
|
@ -953,6 +1002,96 @@ describe('query logic', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('non-regression', () => {
|
||||||
|
|
||||||
|
it('should query by provider super-type in an embedded view', () => {
|
||||||
|
|
||||||
|
@Directive({selector: '[child]'})
|
||||||
|
class Child {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[parent]', providers: [{provide: Child, useExisting: Parent}]})
|
||||||
|
class Parent extends Child {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-cmpt',
|
||||||
|
template:
|
||||||
|
`<ng-template [ngIf]="true"><ng-template [ngIf]="true"><div parent></div></ng-template></ng-template>`
|
||||||
|
})
|
||||||
|
class TestCmpt {
|
||||||
|
@ViewChildren(Child) instances !: QueryList<Child>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TestCmpt, Parent, Child]});
|
||||||
|
const fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.componentInstance.instances.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should flatten multi-provider results', () => {
|
||||||
|
|
||||||
|
class MyClass {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'with-multi-provider',
|
||||||
|
template: '',
|
||||||
|
providers:
|
||||||
|
[{provide: MyClass, useExisting: forwardRef(() => WithMultiProvider), multi: true}]
|
||||||
|
})
|
||||||
|
class WithMultiProvider {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'test-cmpt', template: `<with-multi-provider></with-multi-provider>`})
|
||||||
|
class TestCmpt {
|
||||||
|
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
|
||||||
|
const fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.componentInstance.queryResults.length).toBe(1);
|
||||||
|
expect(fixture.componentInstance.queryResults.first).toBeAnInstanceOf(WithMultiProvider);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should flatten multi-provider results when crossing ng-template', () => {
|
||||||
|
|
||||||
|
class MyClass {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'with-multi-provider',
|
||||||
|
template: '',
|
||||||
|
providers:
|
||||||
|
[{provide: MyClass, useExisting: forwardRef(() => WithMultiProvider), multi: true}]
|
||||||
|
})
|
||||||
|
class WithMultiProvider {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'test-cmpt',
|
||||||
|
template: `
|
||||||
|
<ng-template [ngIf]="true"><with-multi-provider></with-multi-provider></ng-template>
|
||||||
|
<with-multi-provider></with-multi-provider>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class TestCmpt {
|
||||||
|
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
|
||||||
|
const fixture = TestBed.createComponent(TestCmpt);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(fixture.componentInstance.queryResults.length).toBe(2);
|
||||||
|
expect(fixture.componentInstance.queryResults.first).toBeAnInstanceOf(WithMultiProvider);
|
||||||
|
expect(fixture.componentInstance.queryResults.last).toBeAnInstanceOf(WithMultiProvider);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function initWithTemplate(compType: Type<any>, template: string) {
|
function initWithTemplate(compType: Type<any>, template: string) {
|
||||||
|
|
|
@ -110,9 +110,6 @@
|
||||||
{
|
{
|
||||||
"name": "PREORDER_HOOK_FLAGS"
|
"name": "PREORDER_HOOK_FLAGS"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "QUERIES"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "RENDERER"
|
"name": "RENDERER"
|
||||||
},
|
},
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
{
|
{
|
||||||
"name": "ChangeDetectionStrategy"
|
"name": "ChangeDetectionStrategy"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "DECLARATION_LCONTAINER"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "DECLARATION_VIEW"
|
"name": "DECLARATION_VIEW"
|
||||||
},
|
},
|
||||||
|
@ -116,6 +119,9 @@
|
||||||
{
|
{
|
||||||
"name": "MONKEY_PATCH_KEY_NAME"
|
"name": "MONKEY_PATCH_KEY_NAME"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "MOVED_VIEWS"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "NATIVE"
|
"name": "NATIVE"
|
||||||
},
|
},
|
||||||
|
@ -437,9 +443,6 @@
|
||||||
{
|
{
|
||||||
"name": "addRemoveViewFromContainer"
|
"name": "addRemoveViewFromContainer"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "addTContainerToQueries"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
},
|
},
|
||||||
|
@ -602,6 +605,9 @@
|
||||||
{
|
{
|
||||||
"name": "destroyViewTree"
|
"name": "destroyViewTree"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "detachMovedView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "detachView"
|
"name": "detachView"
|
||||||
},
|
},
|
||||||
|
@ -1367,6 +1373,9 @@
|
||||||
{
|
{
|
||||||
"name": "trackByIdentity"
|
"name": "trackByIdentity"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "trackMovedView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "unwrapRNode"
|
"name": "unwrapRNode"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1344,378 +1344,6 @@ describe('query', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('view boundaries', () => {
|
|
||||||
|
|
||||||
describe('JS blocks', () => {
|
|
||||||
|
|
||||||
it('should report results in embedded views', () => {
|
|
||||||
let firstEl;
|
|
||||||
/**
|
|
||||||
* % if (exp) {
|
|
||||||
* <div #foo></div>
|
|
||||||
* % }
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵcontainer(0);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(0);
|
|
||||||
{
|
|
||||||
if (ctx.exp) {
|
|
||||||
let rf1 = ɵɵembeddedViewStart(1, 2, 0);
|
|
||||||
{
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', null, ['foo', '']);
|
|
||||||
firstEl = getNativeByIndex(0, getLView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1, 0, [], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmptInstance = renderComponent(Cmpt);
|
|
||||||
const qList = (cmptInstance.query as any);
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
cmptInstance.exp = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
|
|
||||||
cmptInstance.exp = false;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add results from embedded views in the correct order - views and elements mix',
|
|
||||||
() => {
|
|
||||||
let firstEl, lastEl, viewEl;
|
|
||||||
/**
|
|
||||||
* <span #foo></span>
|
|
||||||
* % if (exp) {
|
|
||||||
* <div #foo></div>
|
|
||||||
* % }
|
|
||||||
* <span #foo></span>
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'span', null, ['foo', '']);
|
|
||||||
firstEl = getNativeByIndex(0, getLView());
|
|
||||||
ɵɵcontainer(2);
|
|
||||||
ɵɵelement(3, 'span', null, ['foo', '']);
|
|
||||||
lastEl = getNativeByIndex(3, getLView());
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(2);
|
|
||||||
{
|
|
||||||
if (ctx.exp) {
|
|
||||||
let rf1 = ɵɵembeddedViewStart(1, 2, 0);
|
|
||||||
{
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', null, ['foo', '']);
|
|
||||||
viewEl = getNativeByIndex(0, getLView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
5, 0, [], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmptInstance = renderComponent(Cmpt);
|
|
||||||
const qList = (cmptInstance.query as any);
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
expect(qList.last.nativeElement).toBe(lastEl);
|
|
||||||
|
|
||||||
cmptInstance.exp = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(3);
|
|
||||||
expect(qList.toArray()[0].nativeElement).toBe(firstEl);
|
|
||||||
expect(qList.toArray()[1].nativeElement).toBe(viewEl);
|
|
||||||
expect(qList.toArray()[2].nativeElement).toBe(lastEl);
|
|
||||||
|
|
||||||
cmptInstance.exp = false;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
expect(qList.last.nativeElement).toBe(lastEl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add results from embedded views in the correct order - views side by side', () => {
|
|
||||||
let firstEl, lastEl;
|
|
||||||
/**
|
|
||||||
* % if (exp1) {
|
|
||||||
* <div #foo></div>
|
|
||||||
* % } if (exp2) {
|
|
||||||
* <span #foo></span>
|
|
||||||
* % }
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵcontainer(0);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(0);
|
|
||||||
{
|
|
||||||
if (ctx.exp1) {
|
|
||||||
let rf0 = ɵɵembeddedViewStart(0, 2, 0);
|
|
||||||
{
|
|
||||||
if (rf0 & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', null, ['foo', '']);
|
|
||||||
firstEl = getNativeByIndex(0, getLView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
if (ctx.exp2) {
|
|
||||||
let rf1 = ɵɵembeddedViewStart(1, 2, 0);
|
|
||||||
{
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'span', null, ['foo', '']);
|
|
||||||
lastEl = getNativeByIndex(0, getLView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1, 0, [], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmptInstance = renderComponent(Cmpt);
|
|
||||||
const qList = (cmptInstance.query as any);
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
cmptInstance.exp2 = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
expect(qList.last.nativeElement).toBe(lastEl);
|
|
||||||
|
|
||||||
cmptInstance.exp1 = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
expect(qList.last.nativeElement).toBe(lastEl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add results from embedded views in the correct order - nested views', () => {
|
|
||||||
let firstEl, lastEl;
|
|
||||||
/**
|
|
||||||
* % if (exp1) {
|
|
||||||
* <div #foo></div>
|
|
||||||
* % if (exp2) {
|
|
||||||
* <span #foo></span>
|
|
||||||
* }
|
|
||||||
* % }
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') query;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵcontainer(0);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(0);
|
|
||||||
{
|
|
||||||
if (ctx.exp1) {
|
|
||||||
let rf0 = ɵɵembeddedViewStart(0, 3, 0);
|
|
||||||
{
|
|
||||||
if (rf0 & RenderFlags.Create) {
|
|
||||||
ɵɵelement(0, 'div', null, ['foo', '']);
|
|
||||||
firstEl = getNativeByIndex(0, getLView());
|
|
||||||
ɵɵcontainer(2);
|
|
||||||
}
|
|
||||||
if (rf0 & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(2);
|
|
||||||
{
|
|
||||||
if (ctx.exp2) {
|
|
||||||
let rf2 = ɵɵembeddedViewStart(0, 2, 0);
|
|
||||||
{
|
|
||||||
if (rf2) {
|
|
||||||
ɵɵelement(0, 'span', null, ['foo', '']);
|
|
||||||
lastEl = getNativeByIndex(0, getLView());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1, 0, [], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵviewQuery(['foo'], true, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.query = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmptInstance = renderComponent(Cmpt);
|
|
||||||
const qList = (cmptInstance.query as any);
|
|
||||||
expect(qList.length).toBe(0);
|
|
||||||
|
|
||||||
cmptInstance.exp1 = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(1);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
|
|
||||||
cmptInstance.exp2 = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(qList.length).toBe(2);
|
|
||||||
expect(qList.first.nativeElement).toBe(firstEl);
|
|
||||||
expect(qList.last.nativeElement).toBe(lastEl);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* What is tested here can't be achieved in the Renderer2 as all view queries are deep by
|
|
||||||
* default and can't be marked as shallow by a user.
|
|
||||||
*/
|
|
||||||
it('should support combination of deep and shallow queries', () => {
|
|
||||||
/**
|
|
||||||
* % if (exp) { ">
|
|
||||||
* <div #foo>
|
|
||||||
* <div #foo></div>
|
|
||||||
* </div>
|
|
||||||
* % }
|
|
||||||
* <span #foo></span>
|
|
||||||
* class Cmpt {
|
|
||||||
* @ViewChildren('foo') deep;
|
|
||||||
* @ViewChildren('foo') shallow;
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const Cmpt = createComponent(
|
|
||||||
'cmpt',
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵcontainer(0);
|
|
||||||
ɵɵelement(1, 'span', null, ['foo', '']);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵcontainerRefreshStart(0);
|
|
||||||
{
|
|
||||||
if (ctx.exp) {
|
|
||||||
let rf0 = ɵɵembeddedViewStart(0, 4, 0);
|
|
||||||
{
|
|
||||||
if (rf0 & RenderFlags.Create) {
|
|
||||||
ɵɵelementStart(0, 'div', null, ['foo', '']);
|
|
||||||
{ ɵɵelement(2, 'div', null, ['foo', '']); }
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵembeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ɵɵcontainerRefreshEnd();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
3, 0, [], [],
|
|
||||||
function(rf: RenderFlags, ctx: any) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵviewQuery(['foo'], true, null);
|
|
||||||
ɵɵviewQuery(['foo'], false, null);
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
let tmp: any;
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.deep = tmp as QueryList<any>);
|
|
||||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
|
||||||
(ctx.shallow = tmp as QueryList<any>);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const cmptInstance = renderComponent(Cmpt);
|
|
||||||
const deep = (cmptInstance.deep as any);
|
|
||||||
const shallow = (cmptInstance.shallow as any);
|
|
||||||
expect(deep.length).toBe(1);
|
|
||||||
expect(shallow.length).toBe(1);
|
|
||||||
|
|
||||||
|
|
||||||
cmptInstance.exp = true;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(deep.length).toBe(3);
|
|
||||||
|
|
||||||
// embedded % if blocks should behave the same way as *ngIf, namely they
|
|
||||||
// should match shallow queries on the first level of elements underneath
|
|
||||||
// the embedded view boundary.
|
|
||||||
expect(shallow.length).toBe(2);
|
|
||||||
|
|
||||||
cmptInstance.exp = false;
|
|
||||||
detectChanges(cmptInstance);
|
|
||||||
expect(deep.length).toBe(1);
|
|
||||||
expect(shallow.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('queryList', () => {
|
describe('queryList', () => {
|
||||||
it('should be destroyed when the containing view is destroyed', () => {
|
it('should be destroyed when the containing view is destroyed', () => {
|
||||||
let queryInstance: QueryList<any>;
|
let queryInstance: QueryList<any>;
|
||||||
|
|
|
@ -752,7 +752,7 @@ export declare function ɵɵcontainerRefreshEnd(): void;
|
||||||
|
|
||||||
export declare function ɵɵcontainerRefreshStart(index: number): void;
|
export declare function ɵɵcontainerRefreshStart(index: number): void;
|
||||||
|
|
||||||
export declare function ɵɵcontentQuery<T>(directiveIndex: number, predicate: Type<any> | string[], descend: boolean, read: any): QueryList<T>;
|
export declare function ɵɵcontentQuery<T>(directiveIndex: number, predicate: Type<any> | string[], descend: boolean, read: any): void;
|
||||||
|
|
||||||
export declare const ɵɵdefaultStyleSanitizer: StyleSanitizeFn;
|
export declare const ɵɵdefaultStyleSanitizer: StyleSanitizeFn;
|
||||||
|
|
||||||
|
@ -926,7 +926,7 @@ export declare function ɵɵload<T>(index: number): T;
|
||||||
|
|
||||||
export declare function ɵɵloadContentQuery<T>(): QueryList<T>;
|
export declare function ɵɵloadContentQuery<T>(): QueryList<T>;
|
||||||
|
|
||||||
export declare function ɵɵloadViewQuery<T>(): T;
|
export declare function ɵɵloadViewQuery<T>(): QueryList<T>;
|
||||||
|
|
||||||
export declare function ɵɵnamespaceHTML(): void;
|
export declare function ɵɵnamespaceHTML(): void;
|
||||||
|
|
||||||
|
@ -1115,7 +1115,7 @@ export declare function ɵɵtextInterpolateV(values: any[]): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵupdateSyntheticHostBinding<T>(propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null): TsickleIssue1009;
|
export declare function ɵɵupdateSyntheticHostBinding<T>(propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null): TsickleIssue1009;
|
||||||
|
|
||||||
export declare function ɵɵviewQuery<T>(predicate: Type<any> | string[], descend: boolean, read: any): QueryList<T>;
|
export declare function ɵɵviewQuery<T>(predicate: Type<any> | string[], descend: boolean, read: any): void;
|
||||||
|
|
||||||
export declare const PACKAGE_ROOT_URL: InjectionToken<string>;
|
export declare const PACKAGE_ROOT_URL: InjectionToken<string>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue