From c1dacdd89038a1b2ce53cbbc17199536efe19e7e Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 5 Jan 2019 14:43:58 +0200 Subject: [PATCH] fix(ivy): QueryList not instance of exported QueryList (#27942) Fixes Ivy's `QueryList` not being an instance of the exported ViewEnginer `QueryList`. Also reworks `first`, `last` and `length` to be regular properties, rather than setters. Reworking `length` was required to be able to extend the ViewEngine `QueryList`, but I reworked `first` and `last` as well since getters generate a lot more code when transpiled to ES5. These changes fix FW-706. PR Close #27942 --- packages/core/src/render3/index.ts | 1 - packages/core/src/render3/query.ts | 99 ++--------------- .../core/test/render3/host_binding_spec.ts | 4 +- packages/core/test/render3/query_list_spec.ts | 102 ------------------ packages/core/test/render3/query_spec.ts | 4 +- .../core/test/render3/styling/players_spec.ts | 3 +- 6 files changed, 13 insertions(+), 200 deletions(-) delete mode 100644 packages/core/test/render3/query_list_spec.ts diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts index 6e5ba0a271..090b3df349 100644 --- a/packages/core/src/render3/index.ts +++ b/packages/core/src/render3/index.ts @@ -121,7 +121,6 @@ export { } from './pipe'; export { - QueryList, query, queryRefresh, } from './query'; diff --git a/packages/core/src/render3/query.ts b/packages/core/src/render3/query.ts index 4066f151a2..e27f432b64 100644 --- a/packages/core/src/render3/query.ts +++ b/packages/core/src/render3/query.ts @@ -12,7 +12,7 @@ import {Observable} from 'rxjs'; import {EventEmitter} from '../event_emitter'; import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref'; -import {QueryList as viewEngine_QueryList} from '../linker/query_list'; +import {QueryList} from '../linker/query_list'; import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref'; import {Type} from '../type'; import {getSymbolIterator} from '../util'; @@ -27,7 +27,7 @@ import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, u import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query'; import {LView, TVIEW} from './interfaces/view'; import {getIsParent, getLView, getOrCreateCurrentQueries} from './state'; -import {flatten, isContentQueryHost} from './util'; +import {isContentQueryHost} from './util'; import {createElementRef, createTemplateRef} from './view_engine_compatibility'; const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4; @@ -95,9 +95,8 @@ export class LQueries_ implements LQueries { public parent: LQueries_|null, private shallow: LQuery|null, private deep: LQuery|null) {} - track( - queryList: viewEngine_QueryList, predicate: Type|string[], descend?: boolean, - read?: Type): void { + track(queryList: QueryList, predicate: Type|string[], descend?: boolean, read?: Type): + void { if (descend) { this.deep = createQuery(this.deep, queryList, predicate, read != null ? read : null); } else { @@ -355,92 +354,7 @@ function createQuery( }; } -class QueryList_/* implements viewEngine_QueryList */ { - readonly dirty = true; - readonly changes: Observable = new EventEmitter(); - private _values: T[] = []; - /** @internal */ - _valuesTree: any[] = []; - - get length(): number { return this._values.length; } - - get first(): T|null { - let values = this._values; - return values.length ? values[0] : null; - } - - get last(): T|null { - let values = this._values; - return values.length ? values[values.length - 1] : null; - } - - /** - * See - * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) - */ - map(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) - */ - filter(fn: (item: T, index: number, array: T[]) => boolean): T[] { - return this._values.filter(fn); - } - - /** - * See - * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) - */ - find(fn: (item: T, index: number, array: T[]) => boolean): T|undefined { - return this._values.find(fn); - } - - /** - * See - * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) - */ - reduce(fn: (prevValue: U, curValue: T, curIndex: number, array: T[]) => U, init: U): U { - return this._values.reduce(fn, init); - } - - /** - * 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) - */ - some(fn: (value: T, index: number, array: T[]) => boolean): boolean { - return this._values.some(fn); - } - - toArray(): T[] { return this._values.slice(0); } - - [getSymbolIterator()](): Iterator { return (this._values as any)[getSymbolIterator()](); } - - toString(): string { return this._values.toString(); } - - reset(res: (any[]|T)[]): void { - this._values = flatten(res); - (this as{dirty: boolean}).dirty = false; - } - - notifyOnChanges(): void { (this.changes as EventEmitter).emit(this); } - setDirty(): void { (this as{dirty: boolean}).dirty = true; } - destroy(): void { - (this.changes as EventEmitter).complete(); - (this.changes as EventEmitter).unsubscribe(); - } -} - -// NOTE: this hack is here because IQueryList has private members and therefore -// it can't be implemented only extended. -export type QueryList = viewEngine_QueryList; -export const QueryList: typeof viewEngine_QueryList = QueryList_ as any; +type QueryList_ = QueryList& {_valuesTree: any[]}; /** * Creates and returns a QueryList. @@ -459,6 +373,7 @@ export function query( ngDevMode && assertPreviousIsParent(getIsParent()); const queryList = new QueryList(); const queries = getOrCreateCurrentQueries(LQueries_); + (queryList as QueryList_)._valuesTree = []; queries.track(queryList, predicate, descend, read); storeCleanupWithContext(getLView(), queryList, queryList.destroy); if (memoryIndex != null) { @@ -475,7 +390,7 @@ export function query( export function queryRefresh(queryList: QueryList): boolean { const queryListImpl = (queryList as any as QueryList_); if (queryList.dirty) { - queryList.reset(queryListImpl._valuesTree); + queryList.reset(queryListImpl._valuesTree || []); queryList.notifyOnChanges(); return true; } diff --git a/packages/core/test/render3/host_binding_spec.ts b/packages/core/test/render3/host_binding_spec.ts index 0f1e48ddb1..f3a7abf829 100644 --- a/packages/core/test/render3/host_binding_spec.ts +++ b/packages/core/test/render3/host_binding_spec.ts @@ -6,9 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {ElementRef} from '@angular/core'; +import {ElementRef, QueryList} from '@angular/core'; -import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature, QueryList} from '../../src/render3/index'; +import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature} from '../../src/render3/index'; import {allocHostVars, bind, directiveInject, element, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, loadQueryList, registerContentQuery, elementHostAttrs} from '../../src/render3/instructions'; import {query, queryRefresh} from '../../src/render3/query'; import {RenderFlags} from '../../src/render3/interfaces/definition'; diff --git a/packages/core/test/render3/query_list_spec.ts b/packages/core/test/render3/query_list_spec.ts deleted file mode 100644 index 59e3973047..0000000000 --- a/packages/core/test/render3/query_list_spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @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 - */ - -import {QueryList} from '../../src/render3/query'; - -describe('QueryList', () => { - let q: QueryList; - - beforeEach(() => { q = new QueryList(); }); - - describe('dirty and reset', () => { - - it('should be dirty and empty initially', () => { - expect(q.dirty).toBeTruthy(); - expect(q.length).toBe(0); - }); - - it('should be not dirty after reset', () => { - expect(q.dirty).toBeTruthy(); - q.reset([1, 2, 3]); - expect(q.dirty).toBeFalsy(); - expect(q.length).toBe(3); - }); - - }); - - describe('elements access', () => { - - it('should give access to the first / last element', () => { - q.reset([1, 2, 3]); - expect(q.length).toBe(3); - expect(q.first).toBe(1); - expect(q.last).toBe(3); - }); - - it('should return copy of matched elements as an array', () => { - q.reset([1, 2, 3]); - - const result = q.toArray(); - expect(result).toEqual([1, 2, 3]); - - // mutate returned result to make sure that oryginal values in query are not mutated - result.push(4); - expect(q.toArray()).toEqual([1, 2, 3]); - }); - - }); - - describe('array-like methods', () => { - - it('should support map method', () => { - q.reset([1, 2, 3]); - expect(q.map((item: number, idx: number) => { - return item + idx; - })).toEqual([1, 3, 5]); - }); - - it('should support filter method', () => { - q.reset([1, 2, 3]); - expect(q.filter((item: number, idx: number) => { return item > 2; })).toEqual([3]); - }); - - it('should support find method', () => { - q.reset([1, 2, 3]); - expect(q.find((item: number, idx: number) => { return item > 0; })).toBe(1); - }); - - it('should support reduce method', () => { - q.reset([1, 2, 3]); - expect(q.reduce((prevValue: number, curValue: number, curIndex: number) => { - return prevValue + curValue + curIndex; - }, 0)).toBe(9); - }); - - it('should support forEach method', () => { - let itemIdxSum = 0; - q.reset([1, 2, 3]); - q.forEach((item: number, idx: number) => { itemIdxSum += item + idx; }); - expect(itemIdxSum).toBe(9); - }); - - it('should support some method', () => { - q.reset([1, 2, 3]); - expect(q.some((item: number, idx: number) => { return item > 0; })).toBe(true); - }); - - }); - - describe('destroy', () => { - it('should close all subscriptions', () => { - let completed = false; - q.changes.subscribe(() => {}, () => {}, () => { completed = true; }); - q.destroy(); - expect(completed).toBeTruthy(); - }); - }); -}); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index ba1c172e27..505b524a4b 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -7,10 +7,10 @@ */ import {NgForOfContext} from '@angular/common'; -import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core'; +import {ElementRef, QueryList, TemplateRef, ViewContainerRef} from '@angular/core'; import {EventEmitter} from '../..'; -import {AttributeMarker, ProvidersFeature, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index'; +import {AttributeMarker, ProvidersFeature, defineComponent, defineDirective, detectChanges} from '../../src/render3/index'; import {getNativeByIndex} from '../../src/render3/util'; import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template, text} from '../../src/render3/instructions'; diff --git a/packages/core/test/render3/styling/players_spec.ts b/packages/core/test/render3/styling/players_spec.ts index 13029c149d..9739d437c8 100644 --- a/packages/core/test/render3/styling/players_spec.ts +++ b/packages/core/test/render3/styling/players_spec.ts @@ -5,6 +5,7 @@ * 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 */ +import {QueryList} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3'; import {defineComponent, getHostElement} from '../../../src/render3/index'; @@ -12,7 +13,7 @@ import {element, elementEnd, elementStart, elementStyling, elementStylingApply, import {PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player'; import {RElement} from '../../../src/render3/interfaces/renderer'; import {addPlayer, getPlayers} from '../../../src/render3/players'; -import {QueryList, query, queryRefresh} from '../../../src/render3/query'; +import {query, queryRefresh} from '../../../src/render3/query'; import {getOrCreatePlayerContext} from '../../../src/render3/styling/util'; import {ComponentFixture} from '../render_util';