diff --git a/packages/core/src/linker/query_list.ts b/packages/core/src/linker/query_list.ts index a3aec4360f..ae483f8f26 100644 --- a/packages/core/src/linker/query_list.ts +++ b/packages/core/src/linker/query_list.ts @@ -93,20 +93,33 @@ export class QueryList/* implements Iterable */ { return this._results.some(fn); } + /** + * Returns a copy of the internal results list as an Array. + */ toArray(): T[] { return this._results.slice(); } [getSymbolIterator()](): Iterator { return (this._results as any)[getSymbolIterator()](); } toString(): string { return this._results.toString(); } - reset(res: Array): void { - this._results = flatten(res); + /** + * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that + * on change detection, it will not notify of changes to the queries, unless a new change + * occurs. + * + * @param resultsTree The results tree to store + */ + reset(resultsTree: Array): void { + this._results = depthFirstFlatten(resultsTree); (this as{dirty: boolean}).dirty = false; (this as{length: number}).length = this._results.length; (this as{last: T}).last = this._results[this.length - 1]; (this as{first: T}).first = this._results[0]; } + /** + * Triggers a change event by emitting on the `changes` {@link EventEmitter}. + */ notifyOnChanges(): void { (this.changes as EventEmitter).emit(this); } /** internal */ @@ -119,9 +132,9 @@ export class QueryList/* implements Iterable */ { } } -function flatten(list: Array): T[] { +function depthFirstFlatten(list: Array): T[] { return list.reduce((flat: any[], item: T | T[]): T[] => { - const flatItem = Array.isArray(item) ? flatten(item) : item; + const flatItem = Array.isArray(item) ? depthFirstFlatten(item) : item; return (flat).concat(flatItem); }, []); } diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 5ec63a9824..741ce9a99d 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -365,7 +365,9 @@ export function query( /** * 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. + * + * @returns `true` if a query got dirty during change detection or if this is a static query + * resolving in creation mode, `false` otherwise. */ export function queryRefresh(queryList: QueryList): boolean { const queryListImpl = (queryList as any as QueryList_); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index 46c20f5376..2221117b52 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -1603,6 +1603,7 @@ describe('query', () => { }); // https://stackblitz.com/edit/angular-7vvo9j?file=src%2Fapp%2Fapp.component.ts + // https://stackblitz.com/edit/angular-xzwp6n it('should report results when the same TemplateRef is inserted into different ViewContainerRefs', () => { let tpl: TemplateRef<{}>; diff --git a/tools/public_api_guard/core/core.d.ts b/tools/public_api_guard/core/core.d.ts index 329a3a23e5..905f9020d5 100644 --- a/tools/public_api_guard/core/core.d.ts +++ b/tools/public_api_guard/core/core.d.ts @@ -721,7 +721,7 @@ export declare class QueryList { map(fn: (item: T, index: number, array: T[]) => U): U[]; notifyOnChanges(): void; reduce(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U; - reset(res: Array): void; + reset(resultsTree: Array): void; setDirty(): void; some(fn: (value: T, index: number, array: T[]) => boolean): boolean; toArray(): T[];