2017-12-01 14:23:03 -08:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2017-12-20 10:47:22 -08:00
|
|
|
// We are temporarily importing the existing viewEngine_from core so we can be sure we are
|
2017-12-14 18:05:41 -08:00
|
|
|
// correctly implementing its interfaces for backwards compatibility.
|
2017-12-14 15:03:46 -08:00
|
|
|
|
2019-01-09 13:49:16 -08:00
|
|
|
import {Type} from '../interface/type';
|
2018-09-21 18:38:13 -07:00
|
|
|
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
|
2019-01-05 14:43:58 +02:00
|
|
|
import {QueryList} from '../linker/query_list';
|
2018-09-21 18:38:13 -07:00
|
|
|
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
|
2019-06-07 10:55:48 +02:00
|
|
|
import {ViewContainerRef} from '../linker/view_container_ref';
|
|
|
|
import {assertDataInRange, assertDefined, throwError} from '../util/assert';
|
|
|
|
import {stringify} from '../util/stringify';
|
2017-12-14 15:03:46 -08:00
|
|
|
|
2019-11-01 13:06:17 -07:00
|
|
|
import {assertFirstCreatePass, assertLContainer} from './assert';
|
2018-11-18 21:04:05 +01:00
|
|
|
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
2019-04-01 15:36:43 -07:00
|
|
|
import {storeCleanupWithContext} from './instructions/shared';
|
2019-06-07 10:55:48 +02:00
|
|
|
import {CONTAINER_HEADER_OFFSET, LContainer, MOVED_VIEWS} from './interfaces/container';
|
2018-11-18 21:04:05 +01:00
|
|
|
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
2018-09-28 17:20:43 -07:00
|
|
|
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
2018-11-18 21:04:05 +01:00
|
|
|
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
2019-06-07 10:55:48 +02:00
|
|
|
import {LQueries, LQuery, TQueries, TQuery, TQueryMetadata, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
|
|
|
import {DECLARATION_LCONTAINER, LView, PARENT, QUERIES, TVIEW, TView} from './interfaces/view';
|
|
|
|
import {assertNodeOfPossibleTypes} from './node_assert';
|
2019-08-02 14:25:52 +02:00
|
|
|
import {getCurrentQueryIndex, getLView, getPreviousOrParentTNode, setCurrentQueryIndex} from './state';
|
|
|
|
import {isCreationMode} from './util/view_utils';
|
2019-06-07 10:55:48 +02:00
|
|
|
import {createContainerRef, createElementRef, createTemplateRef} from './view_engine_compatibility';
|
2018-01-08 20:17:13 -08:00
|
|
|
|
2018-01-11 13:09:21 -08:00
|
|
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4;
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
class LQuery_<T> implements LQuery<T> {
|
|
|
|
matches: (T|null)[]|null = null;
|
|
|
|
constructor(public queryList: QueryList<T>) {}
|
|
|
|
clone(): LQuery<T> { return new LQuery_(this.queryList); }
|
|
|
|
setDirty(): void { this.queryList.setDirty(); }
|
|
|
|
}
|
2018-01-29 15:24:11 +01:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
class LQueries_ implements LQueries {
|
|
|
|
constructor(public queries: LQuery<any>[] = []) {}
|
|
|
|
|
|
|
|
createEmbeddedView(tView: TView): LQueries|null {
|
|
|
|
const tQueries = tView.queries;
|
|
|
|
if (tQueries !== null) {
|
|
|
|
const noOfInheritedQueries =
|
|
|
|
tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
|
2019-08-15 13:42:17 -07:00
|
|
|
const viewLQueries: LQuery<any>[] = [];
|
2019-06-07 10:55:48 +02:00
|
|
|
|
|
|
|
// An embedded view has queries propagated from a declaration view at the beginning of the
|
|
|
|
// TQueries collection and up until a first content query declared in the embedded view. Only
|
|
|
|
// propagated LQueries are created at this point (LQuery corresponding to declared content
|
|
|
|
// queries will be instantiated from the content query instructions for each directive).
|
|
|
|
for (let i = 0; i < noOfInheritedQueries; i++) {
|
|
|
|
const tQuery = tQueries.getByIndex(i);
|
|
|
|
const parentLQuery = this.queries[tQuery.indexInDeclarationView];
|
2019-08-15 13:42:17 -07:00
|
|
|
viewLQueries.push(parentLQuery.clone());
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
2018-01-29 15:24:11 +01:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
return new LQueries_(viewLQueries);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
insertView(tView: TView): void { this.dirtyQueriesWithMatches(tView); }
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-29 15:24:11 +01:00
|
|
|
}
|
2018-01-09 16:43:12 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
class TQueryMetadata_ implements TQueryMetadata {
|
2019-05-24 13:45:02 +02:00
|
|
|
constructor(
|
2019-07-20 12:32:29 +02:00
|
|
|
public predicate: Type<any>|string[], public descendants: boolean, public isStatic: boolean,
|
|
|
|
public read: any = null) {}
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
class TQueries_ implements TQueries {
|
|
|
|
constructor(private queries: TQuery[] = []) {}
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
elementStart(tView: TView, tNode: TNode): void {
|
2019-11-01 13:06:17 -07:00
|
|
|
ngDevMode && assertFirstCreatePass(
|
2019-06-07 10:55:48 +02:00
|
|
|
tView, 'Queries should collect results on the first template pass only');
|
2019-08-15 14:34:30 -07:00
|
|
|
for (let i = 0; i < this.queries.length; i++) {
|
|
|
|
this.queries[i].elementStart(tView, tNode);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
elementEnd(tNode: TNode): void {
|
2019-08-15 14:34:30 -07:00
|
|
|
for (let i = 0; i < this.queries.length; i++) {
|
|
|
|
this.queries[i].elementEnd(tNode);
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
template(tView: TView, tNode: TNode): void {
|
2019-11-01 13:06:17 -07:00
|
|
|
ngDevMode && assertFirstCreatePass(
|
2019-06-07 10:55:48 +02:00
|
|
|
tView, 'Queries should collect results on the first template pass only');
|
2019-08-15 14:34:30 -07:00
|
|
|
for (let i = 0; i < this.queries.length; i++) {
|
|
|
|
this.queries[i].template(tView, tNode);
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
2018-05-28 11:57:36 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
getByIndex(index: number): TQuery {
|
|
|
|
ngDevMode && assertDataInRange(this.queries, index);
|
|
|
|
return this.queries[index];
|
2019-04-18 20:47:37 -07:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
get length(): number { return this.queries.length; }
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
track(tquery: TQuery): void { this.queries.push(tquery); }
|
2018-08-09 10:00:07 -07:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
class TQuery_ implements TQuery {
|
|
|
|
matches: number[]|null = null;
|
|
|
|
indexInDeclarationView = -1;
|
|
|
|
crossesNgTemplate = false;
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
constructor(public metadata: TQueryMetadata, nodeIndex: number = -1) {
|
|
|
|
this._declarationNodeIndex = nodeIndex;
|
|
|
|
}
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
elementStart(tView: TView, tNode: TNode): void {
|
|
|
|
if (this.isApplyingToNode(tNode)) {
|
|
|
|
this.matchTNode(tView, tNode);
|
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
elementEnd(tNode: TNode): void {
|
|
|
|
if (this._declarationNodeIndex === tNode.index) {
|
|
|
|
this._appliesToNextNode = false;
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
template(tView: TView, tNode: TNode): void { this.elementStart(tView, tNode); }
|
2019-01-29 16:08:47 +01:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
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);
|
2019-01-29 16:08:47 +01:00
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
return null;
|
2018-08-09 10:00:07 -07:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
private isApplyingToNode(tNode: TNode): boolean {
|
|
|
|
if (this._appliesToNextNode && this.metadata.descendants === false) {
|
|
|
|
return this._declarationNodeIndex === (tNode.parent ? tNode.parent.index : -1);
|
|
|
|
}
|
|
|
|
return this._appliesToNextNode;
|
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
private matchTNode(tView: TView, tNode: TNode): void {
|
|
|
|
if (Array.isArray(this.metadata.predicate)) {
|
2019-10-09 17:17:52 +02:00
|
|
|
const localNames = this.metadata.predicate;
|
2019-07-19 11:30:06 +02:00
|
|
|
for (let i = 0; i < localNames.length; i++) {
|
|
|
|
this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, localNames[i]));
|
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
} else {
|
|
|
|
const typePredicate = this.metadata.predicate as any;
|
|
|
|
if (typePredicate === ViewEngine_TemplateRef) {
|
2019-07-19 11:30:06 +02:00
|
|
|
if (tNode.type === TNodeType.Container) {
|
|
|
|
this.matchTNodeWithReadOption(tView, tNode, -1);
|
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
} else {
|
2019-07-19 11:30:06 +02:00
|
|
|
this.matchTNodeWithReadOption(
|
|
|
|
tView, tNode, locateDirectiveOrProvider(tNode, tView, typePredicate, false, false));
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
private matchTNodeWithReadOption(tView: TView, tNode: TNode, nodeMatchIdx: number|null): void {
|
|
|
|
if (nodeMatchIdx !== null) {
|
|
|
|
const read = this.metadata.read;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private addMatch(tNodeIdx: number, matchIdx: number) {
|
|
|
|
if (this.matches === null) {
|
|
|
|
this.matches = [tNodeIdx, matchIdx];
|
|
|
|
} else {
|
|
|
|
this.matches.push(tNodeIdx, matchIdx);
|
|
|
|
}
|
|
|
|
}
|
2019-01-29 16:08:47 +01:00
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
|
2017-12-20 17:35:03 +01:00
|
|
|
/**
|
|
|
|
* Iterates over local names for a given node and returns directive index
|
|
|
|
* (or -1 if a local name points to an element).
|
|
|
|
*
|
2018-01-08 20:17:13 -08:00
|
|
|
* @param tNode static data of a node to check
|
2017-12-20 17:35:03 +01:00
|
|
|
* @param selector selector to match
|
|
|
|
* @returns directive index, -1 or null if a selector didn't match any of the local names
|
|
|
|
*/
|
2018-01-08 20:17:13 -08:00
|
|
|
function getIdxOfMatchingSelector(tNode: TNode, selector: string): number|null {
|
|
|
|
const localNames = tNode.localNames;
|
2019-06-07 10:55:48 +02:00
|
|
|
if (localNames !== null) {
|
2017-12-20 17:35:03 +01:00
|
|
|
for (let i = 0; i < localNames.length; i += 2) {
|
|
|
|
if (localNames[i] === selector) {
|
|
|
|
return localNames[i + 1] as number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
function createResultByTNodeType(tNode: TNode, currentView: LView): any {
|
2018-09-28 17:20:43 -07:00
|
|
|
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.ElementContainer) {
|
|
|
|
return createElementRef(ViewEngine_ElementRef, tNode, currentView);
|
2019-06-07 10:55:48 +02:00
|
|
|
} else if (tNode.type === TNodeType.Container) {
|
2018-09-28 17:20:43 -07:00
|
|
|
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
|
|
|
|
function createResultForNode(lView: LView, tNode: TNode, matchingIdx: number, read: any): any {
|
|
|
|
if (matchingIdx === -1) {
|
|
|
|
// if read token and / or strategy is not specified, detect it using appropriate tNode type
|
|
|
|
return createResultByTNodeType(tNode, lView);
|
|
|
|
} 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);
|
2018-10-29 21:55:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
function createSpecialToken(lView: LView, tNode: TNode, read: any): any {
|
|
|
|
if (read === ViewEngine_ElementRef) {
|
|
|
|
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)}.`);
|
2018-10-29 21:55:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 20:47:37 -07:00
|
|
|
/**
|
2019-06-07 10:55:48 +02:00
|
|
|
* 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
|
|
|
|
* doesn't change).
|
2019-04-18 20:47:37 -07:00
|
|
|
*/
|
2019-06-07 10:55:48 +02:00
|
|
|
function materializeViewResults<T>(lView: LView, tQuery: TQuery, queryIndex: number): (T | null)[] {
|
|
|
|
const lQuery = lView[QUERIES] !.queries ![queryIndex];
|
|
|
|
if (lQuery.matches === null) {
|
|
|
|
const tViewData = lView[TVIEW].data;
|
|
|
|
const tQueryMatches = tQuery.matches !;
|
2019-08-15 13:42:17 -07:00
|
|
|
const result: T|null[] = [];
|
2019-06-07 10:55:48 +02:00
|
|
|
for (let i = 0; i < tQueryMatches.length; i += 2) {
|
|
|
|
const matchedNodeIdx = tQueryMatches[i];
|
|
|
|
if (matchedNodeIdx < 0) {
|
|
|
|
// we at the <ng-template> marker which might have results in views created based on this
|
|
|
|
// <ng-template> - those results will be in separate views though, so here we just leave
|
|
|
|
// null as a placeholder
|
2019-08-15 13:42:17 -07:00
|
|
|
result.push(null);
|
2018-10-29 21:55:58 -07:00
|
|
|
} else {
|
2019-06-07 10:55:48 +02:00
|
|
|
ngDevMode && assertDataInRange(tViewData, matchedNodeIdx);
|
|
|
|
const tNode = tViewData[matchedNodeIdx] as TNode;
|
2019-08-15 13:42:17 -07:00
|
|
|
result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read));
|
2017-12-12 14:42:28 +01:00
|
|
|
}
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
lQuery.matches = result;
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
return lQuery.matches;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
/**
|
|
|
|
* A helper function that collects (already materialized) query results from a tree of views,
|
|
|
|
* starting with a provided LView.
|
|
|
|
*/
|
2019-07-19 11:30:06 +02:00
|
|
|
function collectQueryResults<T>(lView: LView, queryIndex: number, result: T[]): T[] {
|
|
|
|
const tQuery = lView[TVIEW].queries !.getByIndex(queryIndex);
|
|
|
|
const tQueryMatches = tQuery.matches;
|
|
|
|
if (tQueryMatches !== null) {
|
|
|
|
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]) {
|
|
|
|
collectQueryResults(embeddedLView, childQueryIndex, result);
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2019-07-19 11:30:06 +02:00
|
|
|
// collect matches for views created from this declaration container and inserted into
|
|
|
|
// different containers
|
|
|
|
if (declarationLContainer[MOVED_VIEWS] !== null) {
|
2019-08-15 14:34:30 -07:00
|
|
|
const embeddedLViews = declarationLContainer[MOVED_VIEWS] !;
|
|
|
|
for (let i = 0; i < embeddedLViews.length; i++) {
|
|
|
|
collectQueryResults(embeddedLViews[i], childQueryIndex, result);
|
2019-06-07 10:55:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refreshes a query by combining matches from all active views and removing matches from deleted
|
|
|
|
* views.
|
2019-03-15 13:18:34 -07:00
|
|
|
*
|
|
|
|
* @returns `true` if a query got dirty during change detection or if this is a static query
|
|
|
|
* resolving in creation mode, `false` otherwise.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2018-01-17 17:55:55 +01:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵqueryRefresh(queryList: QueryList<any>): boolean {
|
2019-06-07 10:55:48 +02:00
|
|
|
const lView = getLView();
|
|
|
|
const queryIndex = getCurrentQueryIndex();
|
2019-02-18 17:33:59 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
setCurrentQueryIndex(queryIndex + 1);
|
|
|
|
|
|
|
|
const tQuery = getTQuery(lView[TVIEW], queryIndex);
|
2019-08-02 14:25:52 +02:00
|
|
|
if (queryList.dirty && (isCreationMode(lView) === tQuery.metadata.isStatic)) {
|
2019-06-07 10:55:48 +02:00
|
|
|
if (tQuery.matches === null) {
|
|
|
|
queryList.reset([]);
|
|
|
|
} else {
|
2019-07-19 11:30:06 +02:00
|
|
|
const result = tQuery.crossesNgTemplate ? collectQueryResults(lView, queryIndex, []) :
|
2019-06-07 10:55:48 +02:00
|
|
|
materializeViewResults(lView, tQuery, queryIndex);
|
|
|
|
queryList.reset(result);
|
|
|
|
queryList.notifyOnChanges();
|
|
|
|
}
|
2018-01-17 17:55:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
|
2018-01-17 17:55:55 +01:00
|
|
|
return false;
|
2018-10-02 21:12:26 -07:00
|
|
|
}
|
2019-01-18 18:02:32 -08:00
|
|
|
|
2019-02-18 17:33:59 -08:00
|
|
|
/**
|
|
|
|
* Creates new QueryList for a static view query.
|
|
|
|
*
|
|
|
|
* @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
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-02-18 17:33:59 -08:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵstaticViewQuery<T>(
|
2019-07-20 12:32:29 +02:00
|
|
|
predicate: Type<any>| string[], descend: boolean, read?: any): void {
|
2019-06-07 10:55:48 +02:00
|
|
|
viewQueryInternal(getLView(), predicate, descend, read, true);
|
2019-02-18 17:33:59 -08:00
|
|
|
}
|
|
|
|
|
2019-01-18 18:02:32 -08:00
|
|
|
/**
|
|
|
|
* Creates new QueryList, stores the reference in LView and returns QueryList.
|
|
|
|
*
|
|
|
|
* @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
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-01-18 18:02:32 -08:00
|
|
|
*/
|
2019-07-20 12:32:29 +02:00
|
|
|
export function ɵɵviewQuery<T>(predicate: Type<any>| string[], descend: boolean, read?: any): void {
|
2019-06-07 10:55:48 +02:00
|
|
|
viewQueryInternal(getLView(), predicate, descend, read, false);
|
2019-05-21 12:04:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function viewQueryInternal<T>(
|
2019-06-07 10:55:48 +02:00
|
|
|
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any,
|
|
|
|
isStatic: boolean): void {
|
|
|
|
const tView = lView[TVIEW];
|
2019-11-01 13:06:17 -07:00
|
|
|
if (tView.firstCreatePass) {
|
2019-07-20 12:32:29 +02:00
|
|
|
createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), -1);
|
2019-06-07 10:55:48 +02:00
|
|
|
if (isStatic) {
|
|
|
|
tView.staticViewQueries = true;
|
|
|
|
}
|
2019-01-18 18:02:32 -08:00
|
|
|
}
|
2019-06-07 10:55:48 +02:00
|
|
|
createLQuery<T>(lView);
|
2019-01-18 18:02:32 -08:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:54:43 -08:00
|
|
|
/**
|
|
|
|
* Registers a QueryList, associated with a content query, for later refresh (part of a view
|
|
|
|
* refresh).
|
|
|
|
*
|
|
|
|
* @param directiveIndex Current directive index
|
|
|
|
* @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>
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-01-23 11:54:43 -08:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵcontentQuery<T>(
|
2019-07-20 12:32:29 +02:00
|
|
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read?: any): void {
|
2019-06-07 10:55:48 +02:00
|
|
|
contentQueryInternal(
|
|
|
|
getLView(), predicate, descend, read, false, getPreviousOrParentTNode(), directiveIndex);
|
2019-01-23 11:54:43 -08:00
|
|
|
}
|
|
|
|
|
2019-02-18 18:18:56 -08:00
|
|
|
/**
|
|
|
|
* Registers a QueryList, associated with a static content query, for later refresh
|
|
|
|
* (part of a view refresh).
|
|
|
|
*
|
|
|
|
* @param directiveIndex Current directive index
|
|
|
|
* @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>
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-02-18 18:18:56 -08:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵstaticContentQuery<T>(
|
2019-07-20 12:32:29 +02:00
|
|
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean, read?: any): void {
|
2019-06-07 10:55:48 +02:00
|
|
|
contentQueryInternal(
|
|
|
|
getLView(), predicate, descend, read, true, getPreviousOrParentTNode(), directiveIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
function contentQueryInternal<T>(
|
|
|
|
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, isStatic: boolean,
|
|
|
|
tNode: TNode, directiveIndex: number): void {
|
2019-05-21 12:04:17 +02:00
|
|
|
const tView = lView[TVIEW];
|
2019-11-01 13:06:17 -07:00
|
|
|
if (tView.firstCreatePass) {
|
2019-07-20 12:32:29 +02:00
|
|
|
createTQuery(tView, new TQueryMetadata_(predicate, descend, isStatic, read), tNode.index);
|
2019-06-07 10:55:48 +02:00
|
|
|
saveContentQueryAndDirectiveIndex(tView, directiveIndex);
|
|
|
|
if (isStatic) {
|
|
|
|
tView.staticContentQueries = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
createLQuery<T>(lView);
|
2019-02-18 18:18:56 -08:00
|
|
|
}
|
|
|
|
|
2019-04-04 11:41:52 -07:00
|
|
|
/**
|
2019-08-12 11:27:18 +03:00
|
|
|
* Loads a QueryList corresponding to the current view or content query.
|
2019-04-04 11:41:52 -07:00
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-04-04 11:41:52 -07:00
|
|
|
*/
|
2019-08-12 11:27:18 +03:00
|
|
|
export function ɵɵloadQuery<T>(): QueryList<T> {
|
2019-06-07 10:55:48 +02:00
|
|
|
return loadQueryInternal<T>(getLView(), getCurrentQueryIndex());
|
|
|
|
}
|
|
|
|
|
|
|
|
function loadQueryInternal<T>(lView: LView, queryIndex: number): QueryList<T> {
|
2019-01-23 11:54:43 -08:00
|
|
|
ngDevMode &&
|
2019-06-07 10:55:48 +02:00
|
|
|
assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query');
|
|
|
|
ngDevMode && assertDataInRange(lView[QUERIES] !.queries, queryIndex);
|
|
|
|
return lView[QUERIES] !.queries[queryIndex].queryList;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
2019-01-23 11:54:43 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2019-01-23 11:54:43 -08:00
|
|
|
|
2019-06-07 10:55:48 +02:00
|
|
|
function getTQuery(tView: TView, index: number): TQuery {
|
|
|
|
ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery');
|
|
|
|
return tView.queries !.getByIndex(index);
|
2019-02-18 17:33:59 -08:00
|
|
|
}
|