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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user