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
This commit is contained in:
parent
9de9c8ad03
commit
c1dacdd890
|
@ -121,7 +121,6 @@ export {
|
|||
} from './pipe';
|
||||
|
||||
export {
|
||||
QueryList,
|
||||
query,
|
||||
queryRefresh,
|
||||
} from './query';
|
||||
|
|
|
@ -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<any>|null,
|
||||
private deep: LQuery<any>|null) {}
|
||||
|
||||
track<T>(
|
||||
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
||||
read?: Type<T>): void {
|
||||
track<T>(queryList: QueryList<T>, predicate: Type<T>|string[], descend?: boolean, read?: Type<T>):
|
||||
void {
|
||||
if (descend) {
|
||||
this.deep = createQuery(this.deep, queryList, predicate, read != null ? read : null);
|
||||
} else {
|
||||
|
@ -355,92 +354,7 @@ function createQuery<T>(
|
|||
};
|
||||
}
|
||||
|
||||
class QueryList_<T>/* implements viewEngine_QueryList<T> */ {
|
||||
readonly dirty = true;
|
||||
readonly changes: Observable<T> = 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<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)
|
||||
*/
|
||||
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<U>(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<T> { 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<any>).emit(this); }
|
||||
setDirty(): void { (this as{dirty: boolean}).dirty = true; }
|
||||
destroy(): void {
|
||||
(this.changes as EventEmitter<any>).complete();
|
||||
(this.changes as EventEmitter<any>).unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this hack is here because IQueryList has private members and therefore
|
||||
// it can't be implemented only extended.
|
||||
export type QueryList<T> = viewEngine_QueryList<T>;
|
||||
export const QueryList: typeof viewEngine_QueryList = QueryList_ as any;
|
||||
type QueryList_<T> = QueryList<T>& {_valuesTree: any[]};
|
||||
|
||||
/**
|
||||
* Creates and returns a QueryList.
|
||||
|
@ -459,6 +373,7 @@ export function query<T>(
|
|||
ngDevMode && assertPreviousIsParent(getIsParent());
|
||||
const queryList = new QueryList<T>();
|
||||
const queries = getOrCreateCurrentQueries(LQueries_);
|
||||
(queryList as QueryList_<T>)._valuesTree = [];
|
||||
queries.track(queryList, predicate, descend, read);
|
||||
storeCleanupWithContext(getLView(), queryList, queryList.destroy);
|
||||
if (memoryIndex != null) {
|
||||
|
@ -475,7 +390,7 @@ export function query<T>(
|
|||
export function queryRefresh(queryList: QueryList<any>): boolean {
|
||||
const queryListImpl = (queryList as any as QueryList_<any>);
|
||||
if (queryList.dirty) {
|
||||
queryList.reset(queryListImpl._valuesTree);
|
||||
queryList.reset(queryListImpl._valuesTree || []);
|
||||
queryList.notifyOnChanges();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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<number>;
|
||||
|
||||
beforeEach(() => { q = new QueryList<number>(); });
|
||||
|
||||
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<number>((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<number>((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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
Loading…
Reference in New Issue