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.
|
2018-02-27 17:06:06 -05:00
|
|
|
import {Observable} from 'rxjs';
|
2017-12-14 15:03:46 -08:00
|
|
|
|
2018-01-29 16:06:31 +01:00
|
|
|
import {EventEmitter} from '../event_emitter';
|
2017-12-20 10:47:22 -08:00
|
|
|
import {QueryList as viewEngine_QueryList} from '../linker/query_list';
|
|
|
|
import {Type} from '../type';
|
2018-01-25 16:17:17 +01:00
|
|
|
import {getSymbolIterator} from '../util';
|
2017-12-14 15:03:46 -08:00
|
|
|
|
2018-05-09 16:49:39 -07:00
|
|
|
import {assertDefined, assertEqual} from './assert';
|
2018-01-17 09:45:40 -08:00
|
|
|
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
|
2018-06-05 15:28:15 -07:00
|
|
|
import {assertPreviousIsParent, getCurrentQueries, store, storeCleanupWithContext} from './instructions';
|
2018-04-13 23:02:29 -07:00
|
|
|
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
|
2018-01-11 13:09:21 -08:00
|
|
|
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
2018-03-20 19:06:49 -07:00
|
|
|
import {LContainerNode, LElementNode, LNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
|
2018-01-29 14:51:37 +01:00
|
|
|
import {LQueries, QueryReadType, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
|
2018-01-17 17:55:55 +01:00
|
|
|
import {flatten} from './util';
|
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.
|
|
|
|
*/
|
|
|
|
read: QueryReadType<T>|Type<T>|null;
|
|
|
|
}
|
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
|
|
|
*/
|
2018-01-29 15:10:46 +01:00
|
|
|
export interface LQuery<T> {
|
2017-12-01 14:23:03 -08:00
|
|
|
/**
|
2018-01-29 15:10:46 +01:00
|
|
|
* Next query. Used when queries are stored as a linked list in `LQueries`.
|
2017-12-01 14:23:03 -08:00
|
|
|
*/
|
2018-01-29 15:10:46 +01:00
|
|
|
next: LQuery<any>|null;
|
2017-12-01 14:23:03 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destination to which the value should be added.
|
|
|
|
*/
|
|
|
|
list: QueryList<T>;
|
|
|
|
|
|
|
|
/**
|
2018-01-29 15:24:11 +01:00
|
|
|
* A predicate which determines if a given element/directive should be included in the query
|
|
|
|
* results.
|
2017-12-01 14:23:03 -08:00
|
|
|
*/
|
2018-01-29 15:24:11 +01:00
|
|
|
predicate: QueryPredicate<T>;
|
2017-12-18 15:14:09 +01:00
|
|
|
|
2017-12-01 14:23:03 -08:00
|
|
|
/**
|
|
|
|
* Values which have been located.
|
|
|
|
*
|
2018-01-29 15:24:11 +01:00
|
|
|
* This is what builds up the `QueryList._valuesTree`.
|
2017-12-01 14:23:03 -08:00
|
|
|
*/
|
|
|
|
values: any[];
|
2018-05-28 11:57:36 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
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-01-29 15:10:46 +01:00
|
|
|
shallow: LQuery<any>|null = null;
|
|
|
|
deep: LQuery<any>|null = null;
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
constructor(deep?: LQuery<any>) { this.deep = deep == null ? null : deep; }
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2017-12-14 15:03:46 -08:00
|
|
|
track<T>(
|
2017-12-20 10:47:22 -08:00
|
|
|
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
2018-01-17 09:45:40 -08:00
|
|
|
read?: QueryReadType<T>|Type<T>): void {
|
2017-12-01 14:23:03 -08:00
|
|
|
// TODO(misko): This is not right. In case of inherited state, a calling track will incorrectly
|
|
|
|
// mutate parent.
|
|
|
|
if (descend) {
|
2018-01-29 15:10:46 +01:00
|
|
|
this.deep = createQuery(this.deep, queryList, predicate, read != null ? read : null);
|
2017-12-01 14:23:03 -08:00
|
|
|
} else {
|
2018-01-29 15:10:46 +01:00
|
|
|
this.shallow = createQuery(this.shallow, queryList, predicate, read != null ? read : null);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
child(): LQueries|null {
|
2017-12-01 14:23:03 -08:00
|
|
|
if (this.deep === null) {
|
2018-01-08 20:17:13 -08:00
|
|
|
// if we don't have any deep queries then no need to track anything more.
|
2017-12-01 14:23:03 -08:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (this.shallow === null) {
|
|
|
|
// DeepQuery: We can reuse the current state if the child state would be same as current
|
|
|
|
// state.
|
|
|
|
return this;
|
|
|
|
} else {
|
|
|
|
// We need to create new state
|
2018-01-29 14:51:37 +01:00
|
|
|
return new LQueries_(this.deep);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
container(): LQueries|null {
|
2018-01-29 15:10:46 +01:00
|
|
|
let result: LQuery<any>|null = null;
|
|
|
|
let query = this.deep;
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
while (query) {
|
2018-01-17 17:55:55 +01:00
|
|
|
const containerValues: any[] = []; // prepare room for views
|
2018-01-29 15:10:46 +01:00
|
|
|
query.values.push(containerValues);
|
2018-05-28 11:57:36 +02:00
|
|
|
const clonedQuery: LQuery<any> = {
|
|
|
|
next: null,
|
|
|
|
list: query.list,
|
|
|
|
predicate: query.predicate,
|
|
|
|
values: containerValues,
|
|
|
|
containerValues: null
|
|
|
|
};
|
2018-01-29 15:10:46 +01:00
|
|
|
clonedQuery.next = result;
|
|
|
|
result = clonedQuery;
|
|
|
|
query = query.next;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
return result ? new LQueries_(result) : null;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-05-28 11:57:36 +02:00
|
|
|
createView(): LQueries|null {
|
2018-01-29 15:10:46 +01:00
|
|
|
let result: LQuery<any>|null = null;
|
|
|
|
let query = this.deep;
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
while (query) {
|
2018-05-28 11:57:36 +02:00
|
|
|
const clonedQuery: LQuery<any> = {
|
|
|
|
next: null,
|
|
|
|
list: query.list,
|
|
|
|
predicate: query.predicate,
|
|
|
|
values: [],
|
|
|
|
containerValues: query.values
|
|
|
|
};
|
2018-01-29 15:10:46 +01:00
|
|
|
clonedQuery.next = result;
|
|
|
|
result = clonedQuery;
|
|
|
|
query = query.next;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-29 14:51:37 +01:00
|
|
|
return result ? new LQueries_(result) : null;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-05-28 11:57:36 +02:00
|
|
|
insertView(index: number): void {
|
|
|
|
let query = this.deep;
|
|
|
|
while (query) {
|
|
|
|
ngDevMode &&
|
2018-05-09 16:49:39 -07:00
|
|
|
assertDefined(
|
2018-05-28 11:57:36 +02:00
|
|
|
query.containerValues, 'View queries need to have a pointer to container values.');
|
|
|
|
query.containerValues !.splice(index, 0, query.values);
|
|
|
|
query = query.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-13 19:34:46 -08:00
|
|
|
addNode(node: LNode): void {
|
2017-12-01 14:23:03 -08:00
|
|
|
add(this.shallow, node);
|
|
|
|
add(this.deep, node);
|
|
|
|
}
|
|
|
|
|
2018-01-17 17:55:55 +01:00
|
|
|
removeView(index: number): void {
|
2018-01-29 15:10:46 +01:00
|
|
|
let query = this.deep;
|
|
|
|
while (query) {
|
2018-05-28 11:57:36 +02:00
|
|
|
ngDevMode &&
|
2018-05-09 16:49:39 -07:00
|
|
|
assertDefined(
|
2018-05-28 11:57:36 +02:00
|
|
|
query.containerValues, 'View queries need to have a pointer to container values.');
|
|
|
|
const removed = query.containerValues !.splice(index, 1);
|
2018-01-17 17:55:55 +01:00
|
|
|
|
|
|
|
// mark a query as dirty only when removed view had matching modes
|
|
|
|
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
|
|
|
if (removed[0].length) {
|
2018-01-29 15:10:46 +01:00
|
|
|
query.list.setDirty();
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
query = query.next;
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
2017-12-01 14:23:03 -08: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Iterates over all the directives for a node and returns index of a directive for a given type.
|
|
|
|
*
|
|
|
|
* @param node Node on which directives are present.
|
|
|
|
* @param type Type of a directive to look for.
|
|
|
|
* @returns Index of a found directive or null when none found.
|
|
|
|
*/
|
2018-04-12 14:52:00 -07:00
|
|
|
function getIdxOfMatchingDirective(node: LNode, type: Type<any>): number|null {
|
2018-03-21 15:10:34 -07:00
|
|
|
const defs = node.view.tView.directives !;
|
2018-05-16 05:56:01 -07:00
|
|
|
const flags = node.tNode.flags;
|
2018-04-12 14:52:00 -07:00
|
|
|
const count = flags & TNodeFlags.DirectiveCountMask;
|
|
|
|
const start = flags >> TNodeFlags.DirectiveStartingIndexShift;
|
|
|
|
const end = start + count;
|
|
|
|
for (let i = start; i < end; i++) {
|
2018-04-13 23:02:29 -07:00
|
|
|
const def = defs[i] as DirectiveDef<any>;
|
2018-04-12 14:52:00 -07:00
|
|
|
if (def.type === type && def.diPublic) {
|
2017-12-20 17:35:03 +01:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function readFromNodeInjector(
|
2018-01-24 15:33:45 +01:00
|
|
|
nodeInjector: LInjector, node: LNode, read: QueryReadType<any>| Type<any>,
|
|
|
|
directiveIdx: number): any {
|
2018-01-17 09:45:40 -08:00
|
|
|
if (read instanceof ReadFromInjectorFn) {
|
|
|
|
return read.read(nodeInjector, node, directiveIdx);
|
2017-12-20 17:35:03 +01:00
|
|
|
} else {
|
2018-04-12 14:52:00 -07:00
|
|
|
const matchingIdx = getIdxOfMatchingDirective(node, read as Type<any>);
|
2017-12-20 17:35:03 +01:00
|
|
|
if (matchingIdx !== null) {
|
2018-03-21 15:10:34 -07:00
|
|
|
return node.view.directives ![matchingIdx];
|
2017-12-19 16:51:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
function add(query: LQuery<any>| null, node: LNode) {
|
2018-01-08 20:17:13 -08:00
|
|
|
const nodeInjector = getOrCreateNodeInjectorForNode(node as LElementNode | LContainerNode);
|
2018-01-29 15:10:46 +01:00
|
|
|
while (query) {
|
2018-01-29 15:24:11 +01:00
|
|
|
const predicate = query.predicate;
|
|
|
|
const type = predicate.type;
|
2017-12-01 14:23:03 -08:00
|
|
|
if (type) {
|
2018-04-12 14:52:00 -07:00
|
|
|
const directiveIdx = getIdxOfMatchingDirective(node, type);
|
2017-12-20 17:35:03 +01:00
|
|
|
if (directiveIdx !== null) {
|
2018-01-24 15:33:45 +01:00
|
|
|
// a node is matching a predicate - determine what to read
|
|
|
|
// if read token and / or strategy is not specified, use type as read token
|
2018-01-29 15:24:11 +01:00
|
|
|
const result =
|
|
|
|
readFromNodeInjector(nodeInjector, node, predicate.read || type, directiveIdx);
|
2018-01-24 15:33:45 +01:00
|
|
|
if (result !== null) {
|
2018-01-29 15:10:46 +01:00
|
|
|
addMatch(query, result);
|
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-05-16 05:56:01 -07:00
|
|
|
const directiveIdx = getIdxOfMatchingSelector(node.tNode, selector[i]);
|
2017-12-19 16:51:42 +01:00
|
|
|
if (directiveIdx !== null) {
|
2018-01-24 15:33:45 +01:00
|
|
|
// a node is matching a predicate - determine what to read
|
|
|
|
// note that queries using name selector must specify read strategy
|
2018-05-09 16:49:39 -07:00
|
|
|
ngDevMode && assertDefined(predicate.read, 'the node should have a predicate');
|
2018-01-29 15:24:11 +01:00
|
|
|
const result = readFromNodeInjector(nodeInjector, node, predicate.read !, directiveIdx);
|
2018-01-24 15:33:45 +01:00
|
|
|
if (result !== null) {
|
2018-01-29 15:10:46 +01:00
|
|
|
addMatch(query, result);
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
function addMatch(query: LQuery<any>, matchingValue: any): void {
|
|
|
|
query.values.push(matchingValue);
|
|
|
|
query.list.setDirty();
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:24:11 +01:00
|
|
|
function createPredicate<T>(
|
|
|
|
predicate: Type<T>| string[], read: QueryReadType<T>| 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
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:10:46 +01:00
|
|
|
function createQuery<T>(
|
|
|
|
previous: LQuery<any>| null, queryList: QueryList<T>, predicate: Type<T>| string[],
|
|
|
|
read: QueryReadType<T>| Type<T>| null): LQuery<T> {
|
2017-12-01 14:23:03 -08:00
|
|
|
return {
|
|
|
|
next: previous,
|
|
|
|
list: queryList,
|
2018-01-29 15:24:11 +01:00
|
|
|
predicate: createPredicate(predicate, read),
|
2018-05-28 11:57:36 +02:00
|
|
|
values: (queryList as any as QueryList_<T>)._valuesTree,
|
|
|
|
containerValues: null
|
2017-12-01 14:23:03 -08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-20 10:47:22 -08:00
|
|
|
class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
2018-01-17 17:55:55 +01:00
|
|
|
readonly dirty = true;
|
2018-01-29 16:06:31 +01:00
|
|
|
readonly changes: Observable<T> = new EventEmitter();
|
2018-01-25 16:17:17 +01:00
|
|
|
private _values: T[] = [];
|
2018-01-17 17:55:55 +01:00
|
|
|
/** @internal */
|
|
|
|
_valuesTree: any[] = [];
|
2017-12-01 14:23:03 -08:00
|
|
|
|
2018-01-25 16:17:17 +01:00
|
|
|
get length(): number { return this._values.length; }
|
2017-12-01 14:23:03 -08:00
|
|
|
|
|
|
|
get first(): T|null {
|
2018-01-25 16:17:17 +01:00
|
|
|
let values = this._values;
|
2017-12-01 14:23:03 -08:00
|
|
|
return values.length ? values[0] : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
get last(): T|null {
|
2018-01-25 16:17:17 +01:00
|
|
|
let values = this._values;
|
2017-12-01 14:23:03 -08:00
|
|
|
return values.length ? values[values.length - 1] : null;
|
|
|
|
}
|
|
|
|
|
2018-01-25 16:17:17 +01:00
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
|
|
|
|
*/
|
|
|
|
map<U>(fn: (item: T, index: number, array: T[]) => U): U[] { return this._values.map(fn); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
|
|
|
|
*/
|
2017-12-01 14:23:03 -08:00
|
|
|
filter(fn: (item: T, index: number, array: T[]) => boolean): T[] {
|
2018-01-25 16:17:17 +01:00
|
|
|
return this._values.filter(fn);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2018-01-25 16:17:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
|
|
|
|
*/
|
2017-12-01 14:23:03 -08:00
|
|
|
find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined {
|
2018-01-25 16:17:17 +01:00
|
|
|
return this._values.find(fn);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2018-01-25 16:17:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
|
|
|
|
*/
|
2017-12-01 14:23:03 -08:00
|
|
|
reduce<U>(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U {
|
2018-01-25 16:17:17 +01:00
|
|
|
return this._values.reduce(fn, init);
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
2018-01-25 16:17:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
|
|
|
|
*/
|
|
|
|
forEach(fn: (item: T, index: number, array: T[]) => void): void { this._values.forEach(fn); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See
|
|
|
|
* [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
|
|
|
|
*/
|
2017-12-01 14:23:03 -08:00
|
|
|
some(fn: (value: T, index: number, array: T[]) => boolean): boolean {
|
2018-01-25 16:17:17 +01:00
|
|
|
return this._values.some(fn);
|
2018-01-17 17:55:55 +01:00
|
|
|
}
|
2018-01-25 16:17:17 +01:00
|
|
|
|
|
|
|
toArray(): T[] { return this._values.slice(0); }
|
|
|
|
|
|
|
|
[getSymbolIterator()](): Iterator<T> { return (this._values as any)[getSymbolIterator()](); }
|
|
|
|
|
|
|
|
toString(): string { return this._values.toString(); }
|
|
|
|
|
2018-01-17 17:55:55 +01:00
|
|
|
reset(res: (any[]|T)[]): void {
|
|
|
|
this._values = flatten(res);
|
|
|
|
(this as{dirty: boolean}).dirty = false;
|
|
|
|
}
|
2018-01-25 16:17:17 +01:00
|
|
|
|
2018-01-29 16:06:31 +01:00
|
|
|
notifyOnChanges(): void { (this.changes as EventEmitter<any>).emit(this); }
|
2018-01-17 17:55:55 +01:00
|
|
|
setDirty(): void { (this as{dirty: boolean}).dirty = true; }
|
2018-01-29 16:06:31 +01:00
|
|
|
destroy(): void {
|
|
|
|
(this.changes as EventEmitter<any>).complete();
|
|
|
|
(this.changes as EventEmitter<any>).unsubscribe();
|
|
|
|
}
|
2017-12-01 14:23:03 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: this hack is here because IQueryList has private members and therefore
|
|
|
|
// it can't be implemented only extended.
|
2017-12-20 10:47:22 -08:00
|
|
|
export type QueryList<T> = viewEngine_QueryList<T>;
|
|
|
|
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
|
2018-01-17 17:55:55 +01:00
|
|
|
|
2018-01-30 15:37:01 -08:00
|
|
|
/**
|
|
|
|
* Creates and returns a QueryList.
|
|
|
|
*
|
|
|
|
* @param memoryIndex The index in memory where the QueryList should be saved. If null,
|
|
|
|
* this is is a content query and the QueryList will be saved later through directiveCreate.
|
|
|
|
* @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>
|
|
|
|
*/
|
2018-01-17 17:55:55 +01:00
|
|
|
export function query<T>(
|
2018-01-30 15:37:01 -08:00
|
|
|
memoryIndex: number | null, predicate: Type<any>| string[], descend?: boolean,
|
2018-01-17 17:55:55 +01:00
|
|
|
read?: QueryReadType<T>| Type<T>): QueryList<T> {
|
|
|
|
ngDevMode && assertPreviousIsParent();
|
|
|
|
const queryList = new QueryList<T>();
|
2018-01-29 14:51:37 +01:00
|
|
|
const queries = getCurrentQueries(LQueries_);
|
|
|
|
queries.track(queryList, predicate, descend, read);
|
2018-06-05 15:28:15 -07:00
|
|
|
storeCleanupWithContext(undefined, queryList, queryList.destroy);
|
2018-01-30 15:37:01 -08:00
|
|
|
if (memoryIndex != null) {
|
2018-02-16 16:58:07 -08:00
|
|
|
store(memoryIndex, queryList);
|
2018-01-30 15:37:01 -08:00
|
|
|
}
|
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.
|
|
|
|
* Returns true if a query got dirty during change detection, false otherwise.
|
|
|
|
*/
|
2018-01-29 14:51:37 +01:00
|
|
|
export function queryRefresh(queryList: QueryList<any>): boolean {
|
|
|
|
const queryListImpl = (queryList as any as QueryList_<any>);
|
|
|
|
if (queryList.dirty) {
|
|
|
|
queryList.reset(queryListImpl._valuesTree);
|
|
|
|
queryList.notifyOnChanges();
|
2018-01-17 17:55:55 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|