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-01-23 11:54:43 -08:00
|
|
|
import {assertDataInRange, assertDefined, assertEqual} from '../util/assert';
|
2017-12-14 15:03:46 -08:00
|
|
|
|
2019-01-09 13:49:16 -08:00
|
|
|
import {assertPreviousIsParent} from './assert';
|
2018-11-18 21:04:05 +01:00
|
|
|
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
|
2018-09-28 17:20:43 -07:00
|
|
|
import {NG_ELEMENT_ID} from './fields';
|
2019-05-21 11:28:36 +02:00
|
|
|
import {store} from './instructions/all';
|
2019-04-01 15:36:43 -07:00
|
|
|
import {storeCleanupWithContext} from './instructions/shared';
|
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';
|
2018-10-09 17:30:46 -07:00
|
|
|
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
2019-05-21 12:04:17 +02:00
|
|
|
import {CONTENT_QUERIES, HEADER_OFFSET, LView, QUERIES, TVIEW, TView} from './interfaces/view';
|
2019-05-24 17:47:21 +02:00
|
|
|
import {getCurrentQueryIndex, getIsParent, getLView, getPreviousOrParentTNode, isCreationMode, setCurrentQueryIndex} from './state';
|
|
|
|
import {isContentQueryHost, loadInternal} from './util/view_utils';
|
2018-09-28 17:20:43 -07:00
|
|
|
import {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
|
|
|
|
2018-01-29 15:24:11 +01:00
|
|
|
/**
|
|
|
|
* A predicate which determines if a given element/directive should be included in the query
|
|
|
|
* results.
|
|
|
|
*/
|
|
|
|
export interface QueryPredicate<T> {
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-10-08 16:33:04 -07:00
|
|
|
read: Type<T>|null;
|
2018-01-29 15:24:11 +01:00
|
|
|
}
|
2018-01-09 16:43:12 -08:00
|
|
|
|
2017-12-01 14:23:03 -08:00
|
|
|
/**
|
2018-01-29 15:10:46 +01:00
|
|
|
* An object representing a query, which is a combination of:
|
|
|
|
* - 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
|
2017-12-01 14:23:03 -08:00
|
|
|
*/
|
2019-05-24 13:45:02 +02:00
|
|
|
class LQuery<T> {
|
|
|
|
constructor(
|
|
|
|
/**
|
|
|
|
* Next query. Used when queries are stored as a linked list in `LQueries`.
|
|
|
|
*/
|
|
|
|
public next: LQuery<any>|null,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destination to which the value should be added.
|
|
|
|
*/
|
|
|
|
public list: QueryList<T>,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A predicate which determines if a given element/directive should be included in the query
|
|
|
|
* results.
|
|
|
|
*/
|
|
|
|
public predicate: QueryPredicate<T>,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Values which have been located.
|
|
|
|
* This is what builds up the `QueryList._valuesTree`.
|
|
|
|
*/
|
|
|
|
public values: any[],
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
public containerValues: any[]|null) {}
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
export class LQueries_ implements LQueries {
|
2018-08-09 10:00:07 -07:00
|
|
|
constructor(
|
|
|
|
public parent: LQueries_|null, private shallow: LQuery<any>|null,
|
2019-05-24 17:47:21 +02:00
|
|
|
private deep: LQuery<any>|null, public nodeIndex: number = -1) {}
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2019-01-05 14:43:58 +02:00
|
|
|
track<T>(queryList: QueryList<T>, predicate: Type<T>|string[], descend?: boolean, read?: Type<T>):
|
|
|
|
void {
|
2017-12-01 14:23:03 -08:00
|
|
|
if (descend) {
|
2019-05-21 12:15:38 +02:00
|
|
|
this.deep = createLQuery(this.deep, queryList, predicate, read != null ? read : null);
|
2017-12-01 14:23:03 -08:00
|
|
|
} else {
|
2019-05-21 12:15:38 +02:00
|
|
|
this.shallow = createLQuery(this.shallow, queryList, predicate, read != null ? read : null);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-24 17:47:21 +02:00
|
|
|
clone(tNode: TNode): LQueries {
|
|
|
|
return this.shallow !== null || isContentQueryHost(tNode) ?
|
|
|
|
new LQueries_(this, null, this.deep, tNode.index) :
|
|
|
|
this;
|
|
|
|
}
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
container(): LQueries|null {
|
2018-08-09 10:00:07 -07:00
|
|
|
const shallowResults = copyQueriesToContainer(this.shallow);
|
|
|
|
const deepResults = copyQueriesToContainer(this.deep);
|
|
|
|
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-05-28 11:57:36 +02:00
|
|
|
createView(): LQueries|null {
|
2018-08-09 10:00:07 -07:00
|
|
|
const shallowResults = copyQueriesToView(this.shallow);
|
|
|
|
const deepResults = copyQueriesToView(this.deep);
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-08-09 10:00:07 -07:00
|
|
|
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-05-28 11:57:36 +02:00
|
|
|
insertView(index: number): void {
|
2018-08-09 10:00:07 -07:00
|
|
|
insertView(index, this.shallow);
|
|
|
|
insertView(index, this.deep);
|
2018-05-28 11:57:36 +02:00
|
|
|
}
|
|
|
|
|
2019-02-06 11:56:57 +01:00
|
|
|
addNode(tNode: TElementNode|TContainerNode|TElementContainerNode): void {
|
2019-04-18 20:47:37 -07:00
|
|
|
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);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
2018-06-11 11:51:16 +02:00
|
|
|
removeView(): void {
|
2018-08-09 10:00:07 -07:00
|
|
|
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);
|
2019-05-24 13:45:02 +02:00
|
|
|
result = new LQuery<any>(result, query.list, query.predicate, containerValues, null);
|
2018-08-09 10:00:07 -07:00
|
|
|
query = query.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function copyQueriesToView(query: LQuery<any>| null): LQuery<any>|null {
|
|
|
|
let result: LQuery<any>|null = null;
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-08-09 10:00:07 -07:00
|
|
|
while (query) {
|
2019-05-24 13:45:02 +02:00
|
|
|
result = new LQuery<any>(result, query.list, query.predicate, [], query.values);
|
2018-08-09 10:00:07 -07:00
|
|
|
query = query.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function insertView(index: number, query: LQuery<any>| null) {
|
|
|
|
while (query) {
|
2019-01-29 16:08:47 +01:00
|
|
|
ngDevMode && assertViewQueryhasPointerToDeclarationContainer(query);
|
2018-08-09 10:00:07 -07:00
|
|
|
query.containerValues !.splice(index, 0, query.values);
|
2019-01-29 16:08:47 +01:00
|
|
|
|
|
|
|
// mark a query as dirty only when inserted view had matching modes
|
|
|
|
if (query.values.length) {
|
|
|
|
query.list.setDirty();
|
|
|
|
}
|
|
|
|
|
2018-08-09 10:00:07 -07:00
|
|
|
query = query.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeView(query: LQuery<any>| null) {
|
|
|
|
while (query) {
|
2019-01-29 16:08:47 +01:00
|
|
|
ngDevMode && assertViewQueryhasPointerToDeclarationContainer(query);
|
2018-08-09 10:00:07 -07:00
|
|
|
|
|
|
|
const containerValues = query.containerValues !;
|
|
|
|
const viewValuesIdx = containerValues.indexOf(query.values);
|
|
|
|
const removed = containerValues.splice(viewValuesIdx, 1);
|
|
|
|
|
|
|
|
// mark a query as dirty only when removed view had matching modes
|
|
|
|
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
|
|
|
if (removed[0].length) {
|
|
|
|
query.list.setDirty();
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
2018-08-09 10:00:07 -07:00
|
|
|
|
|
|
|
query = query.next;
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 16:08:47 +01:00
|
|
|
function assertViewQueryhasPointerToDeclarationContainer(query: LQuery<any>) {
|
|
|
|
assertDefined(query.containerValues, 'View queries need to have a pointer to container values.');
|
|
|
|
}
|
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;
|
2017-12-20 17:35:03 +01:00
|
|
|
if (localNames) {
|
|
|
|
for (let i = 0; i < localNames.length; i += 2) {
|
|
|
|
if (localNames[i] === selector) {
|
|
|
|
return localNames[i + 1] as number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-28 17:20:43 -07:00
|
|
|
// TODO: "read" should be an AbstractType (FW-486)
|
2018-11-21 21:14:06 -08:00
|
|
|
function queryByReadToken(read: any, tNode: TNode, currentView: LView): any {
|
2018-09-28 17:20:43 -07:00
|
|
|
const factoryFn = (read as any)[NG_ELEMENT_ID];
|
|
|
|
if (typeof factoryFn === 'function') {
|
|
|
|
return factoryFn();
|
2017-12-20 17:35:03 +01:00
|
|
|
} else {
|
2018-12-13 11:14:33 +01:00
|
|
|
const matchingIdx =
|
|
|
|
locateDirectiveOrProvider(tNode, currentView, read as Type<any>, false, false);
|
2017-12-20 17:35:03 +01:00
|
|
|
if (matchingIdx !== null) {
|
2018-11-18 21:04:05 +01:00
|
|
|
return getNodeInjectable(
|
|
|
|
currentView[TVIEW].data, currentView, matchingIdx, tNode as TElementNode);
|
2017-12-19 16:51:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
function queryByTNodeType(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);
|
|
|
|
}
|
|
|
|
if (tNode.type === TNodeType.Container) {
|
|
|
|
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-10-29 21:55:58 -07:00
|
|
|
function queryByTemplateRef(
|
2018-11-21 21:14:06 -08:00
|
|
|
templateRefToken: ViewEngine_TemplateRef<any>, tNode: TNode, currentView: LView,
|
2018-10-29 21:55:58 -07:00
|
|
|
read: any): any {
|
|
|
|
const templateRefResult = (templateRefToken as any)[NG_ELEMENT_ID]();
|
|
|
|
if (read) {
|
|
|
|
return templateRefResult ? queryByReadToken(read, tNode, currentView) : null;
|
|
|
|
}
|
|
|
|
return templateRefResult;
|
|
|
|
}
|
|
|
|
|
2018-11-21 21:14:06 -08:00
|
|
|
function queryRead(tNode: TNode, currentView: LView, read: any, matchingIdx: number): any {
|
2018-10-29 21:55:58 -07:00
|
|
|
if (read) {
|
|
|
|
return queryByReadToken(read, tNode, currentView);
|
|
|
|
}
|
|
|
|
if (matchingIdx > -1) {
|
2018-11-18 21:04:05 +01:00
|
|
|
return getNodeInjectable(
|
|
|
|
currentView[TVIEW].data, currentView, matchingIdx, tNode as TElementNode);
|
2018-10-29 21:55:58 -07:00
|
|
|
}
|
|
|
|
// if read token and / or strategy is not specified,
|
|
|
|
// detect it using appropriate tNode type
|
|
|
|
return queryByTNodeType(tNode, currentView);
|
|
|
|
}
|
|
|
|
|
2019-04-18 20:47:37 -07:00
|
|
|
/**
|
|
|
|
* Add query matches for a given node.
|
|
|
|
*
|
|
|
|
* @param query The first query in the linked list
|
|
|
|
* @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)
|
|
|
|
*/
|
2018-09-17 14:32:45 -07:00
|
|
|
function add(
|
2019-04-18 20:47:37 -07:00
|
|
|
query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode,
|
|
|
|
insertBeforeContainer: boolean) {
|
2018-11-21 21:14:06 -08:00
|
|
|
const currentView = getLView();
|
2018-09-13 16:07:23 -07:00
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
while (query) {
|
2018-01-29 15:24:11 +01:00
|
|
|
const predicate = query.predicate;
|
2018-10-29 21:55:58 -07:00
|
|
|
const type = predicate.type as any;
|
2017-12-01 14:23:03 -08:00
|
|
|
if (type) {
|
2018-10-29 21:55:58 -07:00
|
|
|
let result = null;
|
|
|
|
if (type === ViewEngine_TemplateRef) {
|
|
|
|
result = queryByTemplateRef(type, tNode, currentView, predicate.read);
|
|
|
|
} else {
|
2018-12-13 11:14:33 +01:00
|
|
|
const matchingIdx = locateDirectiveOrProvider(tNode, currentView, type, false, false);
|
2018-10-29 21:55:58 -07:00
|
|
|
if (matchingIdx !== null) {
|
|
|
|
result = queryRead(tNode, currentView, predicate.read, matchingIdx);
|
|
|
|
}
|
|
|
|
}
|
2018-09-28 17:20:43 -07:00
|
|
|
if (result !== null) {
|
2019-04-18 20:47:37 -07:00
|
|
|
addMatch(query, result, insertBeforeContainer);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2017-12-12 14:42:28 +01:00
|
|
|
} else {
|
2018-01-29 15:24:11 +01:00
|
|
|
const selector = predicate.selector !;
|
2017-12-19 16:51:42 +01:00
|
|
|
for (let i = 0; i < selector.length; i++) {
|
2018-10-29 21:55:58 -07:00
|
|
|
const matchingIdx = getIdxOfMatchingSelector(tNode, selector[i]);
|
|
|
|
if (matchingIdx !== null) {
|
|
|
|
const result = queryRead(tNode, currentView, predicate.read, matchingIdx);
|
2018-01-24 15:33:45 +01:00
|
|
|
if (result !== null) {
|
2019-04-18 20:47:37 -07:00
|
|
|
addMatch(query, result, insertBeforeContainer);
|
2017-12-12 14:42:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2018-01-29 15:10:46 +01:00
|
|
|
query = query.next;
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 20:47:37 -07:00
|
|
|
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,
|
2019-05-15 16:24:29 -07:00
|
|
|
// ensure template node results are unshifted before container results. Otherwise, results inside
|
2019-04-18 20:47:37 -07:00
|
|
|
// embedded views will appear before results on parent template nodes when flattened.
|
2019-05-15 16:24:29 -07:00
|
|
|
insertBeforeViewMatches ? query.values.unshift(matchingValue) : query.values.push(matchingValue);
|
2018-01-29 15:10:46 +01:00
|
|
|
query.list.setDirty();
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-10-08 16:33:04 -07:00
|
|
|
function createPredicate<T>(predicate: Type<T>| string[], read: Type<T>| null): QueryPredicate<T> {
|
2018-01-29 15:24:11 +01:00
|
|
|
const isArray = Array.isArray(predicate);
|
|
|
|
return {
|
|
|
|
type: isArray ? null : predicate as Type<T>,
|
|
|
|
selector: isArray ? predicate as string[] : null,
|
|
|
|
read: read
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-05-21 12:15:38 +02:00
|
|
|
function createLQuery<T>(
|
2018-01-29 15:10:46 +01:00
|
|
|
previous: LQuery<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
2018-10-08 16:33:04 -07:00
|
|
|
read: Type<T>| null): LQuery<T> {
|
2019-05-24 13:45:02 +02:00
|
|
|
return new LQuery(
|
|
|
|
previous, queryList, createPredicate(predicate, read),
|
|
|
|
(queryList as any as QueryList_<T>)._valuesTree, null);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
2019-02-18 17:33:59 -08:00
|
|
|
type QueryList_<T> = QueryList<T>& {_valuesTree: any[], _static: boolean};
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-01-30 15:37:01 -08:00
|
|
|
/**
|
2019-05-21 12:15:38 +02:00
|
|
|
* Creates a QueryList and stores it in LView's collection of active queries (LQueries).
|
2018-01-30 15:37:01 -08:00
|
|
|
*
|
|
|
|
* @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-05-21 12:15:38 +02:00
|
|
|
function createQueryListInLView<T>(
|
2018-09-28 17:20:43 -07:00
|
|
|
// TODO: "read" should be an AbstractType (FW-486)
|
2019-05-24 17:47:21 +02:00
|
|
|
lView: LView, predicate: Type<any>| string[], descend: boolean, read: any, isStatic: boolean,
|
|
|
|
nodeIndex: number): QueryList<T> {
|
2018-11-21 21:14:06 -08:00
|
|
|
ngDevMode && assertPreviousIsParent(getIsParent());
|
2019-02-18 17:33:59 -08:00
|
|
|
const queryList = new QueryList<T>() as QueryList_<T>;
|
2019-05-24 17:47:21 +02:00
|
|
|
const queries = lView[QUERIES] || (lView[QUERIES] = new LQueries_(null, null, null, nodeIndex));
|
2019-02-18 17:33:59 -08:00
|
|
|
queryList._valuesTree = [];
|
2019-05-21 12:04:17 +02:00
|
|
|
queryList._static = isStatic;
|
2018-01-29 14:51:37 +01:00
|
|
|
queries.track(queryList, predicate, descend, read);
|
2019-02-06 11:56:57 +01:00
|
|
|
storeCleanupWithContext(lView, queryList, queryList.destroy);
|
2018-01-17 17:55:55 +01:00
|
|
|
return queryList;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 {
|
2018-01-29 14:51:37 +01:00
|
|
|
const queryListImpl = (queryList as any as QueryList_<any>);
|
2019-02-18 17:33:59 -08:00
|
|
|
const creationMode = isCreationMode();
|
|
|
|
|
|
|
|
// if creation mode and static or update mode and not static
|
|
|
|
if (queryList.dirty && creationMode === queryListImpl._static) {
|
2019-01-05 14:43:58 +02:00
|
|
|
queryList.reset(queryListImpl._valuesTree || []);
|
2018-01-29 14:51:37 +01:00
|
|
|
queryList.notifyOnChanges();
|
2018-01-17 17:55:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
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-02-18 17:33:59 -08:00
|
|
|
// TODO(FW-486): "read" should be an AbstractType
|
|
|
|
predicate: Type<any>| string[], descend: boolean, read: any): void {
|
2019-05-21 12:04:17 +02:00
|
|
|
const lView = getLView();
|
|
|
|
const tView = lView[TVIEW];
|
|
|
|
viewQueryInternal(lView, tView, predicate, descend, read, true);
|
|
|
|
tView.staticViewQueries = 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
|
|
|
|
* @returns QueryList<T>
|
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-05-17 18:49:21 -07:00
|
|
|
export function ɵɵviewQuery<T>(
|
2019-02-18 17:33:59 -08:00
|
|
|
// TODO(FW-486): "read" should be an AbstractType
|
|
|
|
predicate: Type<any>| string[], descend: boolean, read: any): QueryList<T> {
|
2019-01-18 18:02:32 -08:00
|
|
|
const lView = getLView();
|
|
|
|
const tView = lView[TVIEW];
|
2019-05-21 12:04:17 +02:00
|
|
|
return viewQueryInternal(lView, tView, predicate, descend, read, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
function viewQueryInternal<T>(
|
|
|
|
lView: LView, tView: TView, predicate: Type<any>| string[], descend: boolean, read: any,
|
|
|
|
isStatic: boolean): QueryList<T> {
|
2019-01-18 18:02:32 -08:00
|
|
|
if (tView.firstTemplatePass) {
|
|
|
|
tView.expandoStartIndex++;
|
|
|
|
}
|
2019-01-23 11:54:43 -08:00
|
|
|
const index = getCurrentQueryIndex();
|
2019-05-21 12:15:38 +02:00
|
|
|
const queryList: QueryList<T> =
|
2019-05-24 17:47:21 +02:00
|
|
|
createQueryListInLView<T>(lView, predicate, descend, read, isStatic, -1);
|
2019-05-21 12:04:17 +02:00
|
|
|
store(index - HEADER_OFFSET, queryList);
|
2019-01-23 11:54:43 -08:00
|
|
|
setCurrentQueryIndex(index + 1);
|
2019-05-21 12:04:17 +02:00
|
|
|
return queryList;
|
2019-01-18 18:02:32 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-04-04 11:41:52 -07:00
|
|
|
* Loads current View Query and moves the pointer/index to the next View Query in LView.
|
|
|
|
*
|
2019-04-10 13:45:26 -07:00
|
|
|
* @codeGenApi
|
2019-04-04 11:41:52 -07:00
|
|
|
*/
|
2019-05-17 18:49:21 -07:00
|
|
|
export function ɵɵloadViewQuery<T>(): T {
|
2019-01-23 11:54:43 -08:00
|
|
|
const index = getCurrentQueryIndex();
|
|
|
|
setCurrentQueryIndex(index + 1);
|
2019-05-21 11:28:36 +02:00
|
|
|
return loadInternal<T>(getLView(), index - HEADER_OFFSET);
|
2019-01-23 21:11:13 -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-02-18 17:33:59 -08:00
|
|
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
|
2019-02-18 18:18:56 -08:00
|
|
|
// TODO(FW-486): "read" should be an AbstractType
|
2019-02-18 17:33:59 -08:00
|
|
|
read: any): QueryList<T> {
|
2019-01-23 11:54:43 -08:00
|
|
|
const lView = getLView();
|
|
|
|
const tView = lView[TVIEW];
|
2019-05-24 17:47:21 +02:00
|
|
|
const tNode = getPreviousOrParentTNode();
|
|
|
|
return contentQueryInternal(
|
|
|
|
lView, tView, directiveIndex, predicate, descend, read, false, tNode.index);
|
2019-05-21 12:04:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function contentQueryInternal<T>(
|
|
|
|
lView: LView, tView: TView, directiveIndex: number, predicate: Type<any>| string[],
|
|
|
|
descend: boolean,
|
|
|
|
// TODO(FW-486): "read" should be an AbstractType
|
2019-05-24 17:47:21 +02:00
|
|
|
read: any, isStatic: boolean, nodeIndex: number): QueryList<T> {
|
2019-05-21 12:15:38 +02:00
|
|
|
const contentQuery: QueryList<T> =
|
2019-05-24 17:47:21 +02:00
|
|
|
createQueryListInLView<T>(lView, predicate, descend, read, isStatic, nodeIndex);
|
2019-01-23 11:54:43 -08:00
|
|
|
(lView[CONTENT_QUERIES] || (lView[CONTENT_QUERIES] = [])).push(contentQuery);
|
2019-01-30 16:38:59 +01:00
|
|
|
if (tView.firstTemplatePass) {
|
2019-01-23 11:54:43 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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-02-18 18:18:56 -08:00
|
|
|
directiveIndex: number, predicate: Type<any>| string[], descend: boolean,
|
|
|
|
// TODO(FW-486): "read" should be an AbstractType
|
|
|
|
read: any): void {
|
2019-05-21 12:04:17 +02:00
|
|
|
const lView = getLView();
|
|
|
|
const tView = lView[TVIEW];
|
2019-05-24 17:47:21 +02:00
|
|
|
const tNode = getPreviousOrParentTNode();
|
|
|
|
contentQueryInternal(lView, tView, directiveIndex, predicate, descend, read, true, tNode.index);
|
2019-05-21 12:04:17 +02:00
|
|
|
tView.staticContentQueries = true;
|
2019-02-18 18:18:56 -08:00
|
|
|
}
|
|
|
|
|
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-05-17 18:49:21 -07:00
|
|
|
export function ɵɵloadContentQuery<T>(): QueryList<T> {
|
2019-01-23 11:54:43 -08:00
|
|
|
const lView = getLView();
|
|
|
|
ngDevMode &&
|
|
|
|
assertDefined(
|
|
|
|
lView[CONTENT_QUERIES], 'Content QueryList array should be defined if reading a query.');
|
|
|
|
|
|
|
|
const index = getCurrentQueryIndex();
|
|
|
|
ngDevMode && assertDataInRange(lView[CONTENT_QUERIES] !, index);
|
|
|
|
|
|
|
|
setCurrentQueryIndex(index + 1);
|
|
|
|
return lView[CONTENT_QUERIES] ![index];
|
2019-02-18 17:33:59 -08:00
|
|
|
}
|