refactor: remove facade/collection (#14837)

This commit is contained in:
Miško Hevery 2017-03-01 14:10:59 -08:00 committed by Chuck Jazdzewski
parent 4fe0b90948
commit b0e0839075
27 changed files with 149 additions and 244 deletions

View File

@ -6,9 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer, ɵisListLikeIterable as isListLikeIterable} from '@angular/core';
import {isListLikeIterable} from '../facade/collection';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
/** /**

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, flatten, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig} from '../config';
import {ListWrapper} from '../facade/collection';
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers'; import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
import {CompileMetadataResolver} from '../metadata_resolver'; import {CompileMetadataResolver} from '../metadata_resolver';
import {NgModuleCompiler} from '../ng_module_compiler'; import {NgModuleCompiler} from '../ng_module_compiler';
@ -51,7 +50,7 @@ export class AotCompiler {
file => this._compileSrcFile( file => this._compileSrcFile(
file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes,
file.ngModules, file.injectables)); file.ngModules, file.injectables));
return ListWrapper.flatten(sourceModules); return flatten(sourceModules);
}); });
} }

View File

@ -7,9 +7,7 @@
*/ */
import {ChangeDetectionStrategy, ComponentFactory, RendererTypeV2, SchemaMetadata, Type, ViewEncapsulation, ɵLifecycleHooks, ɵreflector} from '@angular/core'; import {ChangeDetectionStrategy, ComponentFactory, RendererTypeV2, SchemaMetadata, Type, ViewEncapsulation, ɵLifecycleHooks, ɵreflector} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol'; import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection';
import {isPresent, stringify} from './facade/lang'; import {isPresent, stringify} from './facade/lang';
import {CssSelector} from './selector'; import {CssSelector} from './selector';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
@ -272,7 +270,7 @@ export class CompileTemplateMetadata {
this.styles = _normalizeArray(styles); this.styles = _normalizeArray(styles);
this.styleUrls = _normalizeArray(styleUrls); this.styleUrls = _normalizeArray(styleUrls);
this.externalStylesheets = _normalizeArray(externalStylesheets); this.externalStylesheets = _normalizeArray(externalStylesheets);
this.animations = animations ? ListWrapper.flatten(animations) : []; this.animations = animations ? flatten(animations) : [];
this.ngContentSelectors = ngContentSelectors || []; this.ngContentSelectors = ngContentSelectors || [];
if (interpolation && interpolation.length != 2) { if (interpolation && interpolation.length != 2) {
throw new Error(`'interpolation' should have a start and an end symbol.`); throw new Error(`'interpolation' should have a start and an end symbol.`);
@ -735,3 +733,10 @@ export class ProviderMeta {
this.multi = !!multi; this.multi = !!multi;
} }
} }
export function flatten<T>(list: Array<T|T[]>): T[] {
return list.reduce((flat: any[], item: T | T[]): T[] => {
const flatItem = Array.isArray(item) ? flatten(item) : item;
return (<T[]>flat).concat(flatItem);
}, []);
}

View File

@ -6,13 +6,13 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵreflector} from '@angular/core'; import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵmerge as merge, ɵreflector} from '@angular/core';
import {ListWrapper, StringMapWrapper} from './facade/collection';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable'; import {CompilerInjectable} from './injectable';
import {splitAtColon} from './util'; import {splitAtColon} from './util';
/* /*
* Resolve a `Type` for {@link Directive}. * Resolve a `Type` for {@link Directive}.
* *
@ -35,7 +35,7 @@ export class DirectiveResolver {
resolve(type: Type<any>, throwIfNotFound = true): Directive { resolve(type: Type<any>, throwIfNotFound = true): Directive {
const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
if (typeMetadata) { if (typeMetadata) {
const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata); const metadata = findLast(typeMetadata, isDirectiveMetadata);
if (metadata) { if (metadata) {
const propertyMetadata = this._reflector.propMetadata(type); const propertyMetadata = this._reflector.propMetadata(type);
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
@ -58,7 +58,7 @@ export class DirectiveResolver {
const queries: {[key: string]: any} = {}; const queries: {[key: string]: any} = {};
Object.keys(propertyMetadata).forEach((propName: string) => { Object.keys(propertyMetadata).forEach((propName: string) => {
const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input); const input = findLast(propertyMetadata[propName], (a) => a instanceof Input);
if (input) { if (input) {
if (input.bindingPropertyName) { if (input.bindingPropertyName) {
inputs.push(`${propName}: ${input.bindingPropertyName}`); inputs.push(`${propName}: ${input.bindingPropertyName}`);
@ -66,7 +66,7 @@ export class DirectiveResolver {
inputs.push(propName); inputs.push(propName);
} }
} }
const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output); const output = findLast(propertyMetadata[propName], (a) => a instanceof Output);
if (output) { if (output) {
if (output.bindingPropertyName) { if (output.bindingPropertyName) {
outputs.push(`${propName}: ${output.bindingPropertyName}`); outputs.push(`${propName}: ${output.bindingPropertyName}`);
@ -94,7 +94,7 @@ export class DirectiveResolver {
const args = hostListener.args || []; const args = hostListener.args || [];
host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`; host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
}); });
const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query); const query = findLast(propertyMetadata[propName], (a) => a instanceof Query);
if (query) { if (query) {
queries[propName] = query; queries[propName] = query;
} }
@ -126,9 +126,8 @@ export class DirectiveResolver {
this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs); this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
const mergedOutputs = const mergedOutputs =
this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs); this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host; const mergedHost = directive.host ? merge(directive.host, host) : host;
const mergedQueries = const mergedQueries = directive.queries ? merge(directive.queries, queries) : queries;
directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries;
if (directive instanceof Component) { if (directive instanceof Component) {
return new Component({ return new Component({
@ -168,3 +167,12 @@ export class DirectiveResolver {
function isDirectiveMetadata(type: any): type is Directive { function isDirectiveMetadata(type: any): type is Directive {
return type instanceof Directive; return type instanceof Directive;
} }
export function findLast<T>(arr: T[], condition: (value: T) => boolean): T {
for (let i = arr.length - 1; i >= 0; i--) {
if (condition(arr[i])) {
return arr[i];
}
}
return null;
}

View File

@ -7,8 +7,7 @@
*/ */
import {NgModule, Type, ɵReflectorReader, ɵreflector} from '@angular/core'; import {NgModule, Type, ɵReflectorReader, ɵreflector} from '@angular/core';
import {findLast} from './directive_resolver';
import {ListWrapper} from './facade/collection';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable'; import {CompilerInjectable} from './injectable';
@ -26,8 +25,7 @@ export class NgModuleResolver {
isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); } isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); }
resolve(type: Type<any>, throwIfNotFound = true): NgModule { resolve(type: Type<any>, throwIfNotFound = true): NgModule {
const ngModuleMeta: NgModule = const ngModuleMeta: NgModule = findLast(this._reflector.annotations(type), _isNgModuleMetadata);
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
if (ngModuleMeta) { if (ngModuleMeta) {
return ngModuleMeta; return ngModuleMeta;

View File

@ -7,8 +7,7 @@
*/ */
import {Pipe, Type, resolveForwardRef, ɵReflectorReader, ɵreflector} from '@angular/core'; import {Pipe, Type, resolveForwardRef, ɵReflectorReader, ɵreflector} from '@angular/core';
import {findLast} from './directive_resolver';
import {ListWrapper} from './facade/collection';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
import {CompilerInjectable} from './injectable'; import {CompilerInjectable} from './injectable';
@ -38,7 +37,7 @@ export class PipeResolver {
resolve(type: Type<any>, throwIfNotFound = true): Pipe { resolve(type: Type<any>, throwIfNotFound = true): Pipe {
const metas = this._reflector.annotations(resolveForwardRef(type)); const metas = this._reflector.annotations(resolveForwardRef(type));
if (metas) { if (metas) {
const annotation = ListWrapper.findLast(metas, _isPipeMetadata); const annotation = findLast(metas, _isPipeMetadata);
if (annotation) { if (annotation) {
return annotation; return annotation;
} }

View File

@ -7,7 +7,6 @@
*/ */
import {ResourceLoader} from '@angular/compiler'; import {ResourceLoader} from '@angular/compiler';
import {ListWrapper} from './facade/collection';
import {isBlank} from './facade/lang'; import {isBlank} from './facade/lang';
/** /**
@ -82,7 +81,7 @@ export class MockResourceLoader extends ResourceLoader {
if (this._expectations.length > 0) { if (this._expectations.length > 0) {
const expectation = this._expectations[0]; const expectation = this._expectations[0];
if (expectation.url == url) { if (expectation.url == url) {
ListWrapper.remove(this._expectations, expectation); remove(this._expectations, expectation);
request.complete(expectation.response); request.complete(expectation.response);
return; return;
} }
@ -129,3 +128,10 @@ class _Expectation {
this.response = response; this.response = response;
} }
} }
function remove<T>(list: T[], el: T): void {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
}
}

View File

@ -14,7 +14,6 @@ import {merge} from 'rxjs/observable/merge';
import {share} from 'rxjs/operator/share'; import {share} from 'rxjs/operator/share';
import {ErrorHandler} from '../src/error_handler'; import {ErrorHandler} from '../src/error_handler';
import {ListWrapper} from '../src/facade/collection';
import {scheduleMicroTask, stringify} from '../src/facade/lang'; import {scheduleMicroTask, stringify} from '../src/facade/lang';
import {isPromise} from '../src/util/lang'; import {isPromise} from '../src/util/lang';
@ -300,7 +299,7 @@ export class PlatformRef_ extends PlatformRef {
if (!exceptionHandler) { if (!exceptionHandler) {
throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?'); throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
} }
moduleRef.onDestroy(() => ListWrapper.remove(this._modules, moduleRef)); moduleRef.onDestroy(() => remove(this._modules, moduleRef));
ngZone.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }}); ngZone.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }});
return _callAndReportToErrorHandler(exceptionHandler, () => { return _callAndReportToErrorHandler(exceptionHandler, () => {
const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus);
@ -489,7 +488,7 @@ export class ApplicationRef_ extends ApplicationRef {
detachView(viewRef: ViewRef): void { detachView(viewRef: ViewRef): void {
const view = (viewRef as InternalViewRef); const view = (viewRef as InternalViewRef);
ListWrapper.remove(this._views, view); remove(this._views, view);
view.detachFromAppRef(); view.detachFromAppRef();
} }
@ -533,7 +532,7 @@ export class ApplicationRef_ extends ApplicationRef {
private _unloadComponent(componentRef: ComponentRef<any>): void { private _unloadComponent(componentRef: ComponentRef<any>): void {
this.detachView(componentRef.hostView); this.detachView(componentRef.hostView);
ListWrapper.remove(this._rootComponents, componentRef); remove(this._rootComponents, componentRef);
} }
tick(): void { tick(): void {
@ -567,3 +566,10 @@ export class ApplicationRef_ extends ApplicationRef {
get isStable(): Observable<boolean> { return this._isStable; } get isStable(): Observable<boolean> { return this._isStable; }
} }
function remove<T>(list: T[], el: T): void {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
}
}

View File

@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {areIterablesEqual, isListLikeIterable} from '../facade/collection'; import {getSymbolIterator, isJsObject, isPrimitive, looseIdentical} from '../facade/lang';
import {isPrimitive, looseIdentical} from '../facade/lang';
export {looseIdentical} from '../facade/lang'; export {looseIdentical} from '../facade/lang';
@ -78,3 +77,38 @@ export class SimpleChange {
*/ */
isFirstChange(): boolean { return this.firstChange; } isFirstChange(): boolean { return this.firstChange; }
} }
export function isListLikeIterable(obj: any): boolean {
if (!isJsObject(obj)) return false;
return Array.isArray(obj) ||
(!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
}
export function areIterablesEqual(
a: any, b: any, comparator: (a: any, b: any) => boolean): boolean {
const iterator1 = a[getSymbolIterator()]();
const iterator2 = b[getSymbolIterator()]();
while (true) {
const item1 = iterator1.next();
const item2 = iterator2.next();
if (item1.done && item2.done) return true;
if (item1.done || item2.done) return false;
if (!comparator(item1.value, item2.value)) return false;
}
}
export function iterateListLike(obj: any, fn: (p: any) => any) {
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
fn(obj[i]);
}
} else {
const iterator = obj[getSymbolIterator()]();
let item: any;
while (!((item = iterator.next()).done)) {
fn(item.value);
}
}
}

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {isListLikeIterable, iterateListLike} from '../../facade/collection';
import {isBlank, looseIdentical, stringify} from '../../facade/lang'; import {isBlank, looseIdentical, stringify} from '../../facade/lang';
import {isListLikeIterable, iterateListLike} from '../change_detection_util';
import {ChangeDetectorRef} from '../change_detector_ref'; import {ChangeDetectorRef} from '../change_detector_ref';
import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, NgIterable, TrackByFunction} from './iterable_differs'; import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, NgIterable, TrackByFunction} from './iterable_differs';

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {StringMapWrapper} from '../../facade/collection';
import {isJsObject, looseIdentical, stringify} from '../../facade/lang'; import {isJsObject, looseIdentical, stringify} from '../../facade/lang';
import {ChangeDetectorRef} from '../change_detector_ref'; import {ChangeDetectorRef} from '../change_detector_ref';

View File

@ -21,7 +21,7 @@ export {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
export * from './zone'; export * from './zone';
export * from './render'; export * from './render';
export * from './linker'; export * from './linker';
export {DebugElement, DebugNode, asNativeElements, getDebugNode} from './debug/debug_node'; export {DebugElement, DebugNode, asNativeElements, getDebugNode, Predicate} from './debug/debug_node';
export {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter} from './testability/testability'; export {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter} from './testability/testability';
export * from './change_detection'; export * from './change_detection';
export * from './platform_core_providers'; export * from './platform_core_providers';
@ -34,7 +34,6 @@ export {ErrorHandler} from './error_handler';
export * from './core_private_export'; export * from './core_private_export';
export {Sanitizer, SecurityContext} from './security'; export {Sanitizer, SecurityContext} from './security';
export * from './codegen_private_exports'; export * from './codegen_private_exports';
export * from './animation/animation_metadata_wrapped'; export * from './animation/animation_metadata_wrapped';
import {AnimationTriggerMetadata} from './animation/animation_metadata_wrapped'; import {AnimationTriggerMetadata} from './animation/animation_metadata_wrapped';

View File

@ -9,6 +9,7 @@
export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS} from './application_ref'; export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS} from './application_ref';
export {APP_ID_RANDOM_PROVIDER as ɵAPP_ID_RANDOM_PROVIDER} from './application_tokens'; export {APP_ID_RANDOM_PROVIDER as ɵAPP_ID_RANDOM_PROVIDER} from './application_tokens';
export {ValueUnwrapper as ɵValueUnwrapper, devModeEqual as ɵdevModeEqual} from './change_detection/change_detection_util'; export {ValueUnwrapper as ɵValueUnwrapper, devModeEqual as ɵdevModeEqual} from './change_detection/change_detection_util';
export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/change_detection_util';
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants'; export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console'; export {Console as ɵConsole} from './console';
export {ERROR_COMPONENT_TYPE as ɵERROR_COMPONENT_TYPE} from './errors'; export {ERROR_COMPONENT_TYPE as ɵERROR_COMPONENT_TYPE} from './errors';
@ -23,4 +24,4 @@ export {ReflectorReader as ɵReflectorReader} from './reflection/reflector_reade
export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types'; export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types';
export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api'; export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api';
export {makeDecorator as ɵmakeDecorator} from './util/decorators'; export {makeDecorator as ɵmakeDecorator} from './util/decorators';
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang'; export {isObservable as ɵisObservable, isPromise as ɵisPromise, merge as ɵmerge} from './util/lang';

View File

@ -7,7 +7,6 @@
*/ */
import {Injector} from '../di'; import {Injector} from '../di';
import {Predicate} from '../facade/collection';
import {RenderDebugInfo} from '../render/api'; import {RenderDebugInfo} from '../render/api';
export class EventListener { constructor(public name: string, public callback: Function){}; } export class EventListener { constructor(public name: string, public callback: Function){}; }
@ -192,3 +191,11 @@ export function indexDebugNode(node: DebugNode) {
export function removeDebugNodeFromIndex(node: DebugNode) { export function removeDebugNodeFromIndex(node: DebugNode) {
_nativeNodeToDebugNode.delete(node.nativeNode); _nativeNodeToDebugNode.delete(node.nativeNode);
} }
/**
* A boolean-valued function over a value, possibly including context information
* regarding that value's position in an array.
*
* @experimental All debugging apis are currently experimental.
*/
export interface Predicate<T> { (value: T, index?: number, array?: T[]): boolean; }

View File

@ -9,7 +9,6 @@
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {EventEmitter} from '../event_emitter'; import {EventEmitter} from '../event_emitter';
import {ListWrapper} from '../facade/collection';
import {getSymbolIterator} from '../facade/lang'; import {getSymbolIterator} from '../facade/lang';
@ -95,7 +94,7 @@ export class QueryList<T>/* implements Iterable<T> */ {
toString(): string { return this._results.toString(); } toString(): string { return this._results.toString(); }
reset(res: Array<T|any[]>): void { reset(res: Array<T|any[]>): void {
this._results = ListWrapper.flatten(res); this._results = flatten(res);
this._dirty = false; this._dirty = false;
} }
@ -107,3 +106,10 @@ export class QueryList<T>/* implements Iterable<T> */ {
/** internal */ /** internal */
get dirty() { return this._dirty; } get dirty() { return this._dirty; }
} }
function flatten<T>(list: Array<T|T[]>): T[] {
return list.reduce((flat: any[], item: T | T[]): T[] => {
const flatItem = Array.isArray(item) ? flatten(item) : item;
return (<T[]>flat).concat(flatItem);
}, []);
}

View File

@ -24,3 +24,18 @@ export function isPromise(obj: any): obj is Promise<any> {
export function isObservable(obj: any | Observable<any>): obj is Observable<any> { export function isObservable(obj: any | Observable<any>): obj is Observable<any> {
return !!(obj && obj[symbolObservable]); return !!(obj && obj[symbolObservable]);
} }
// TODO(misko): replace with Object.assign once we require ES6.
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
const m: {[key: string]: V} = {};
for (const k of Object.keys(m1)) {
m[k] = m1[k];
}
for (const k of Object.keys(m2)) {
m[k] = m2[k];
}
return m;
}

View File

@ -6,13 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {iterateListLike} from '@angular/core/src/change_detection/change_detection_util';
import {QueryList} from '@angular/core/src/linker/query_list'; import {QueryList} from '@angular/core/src/linker/query_list';
import {fakeAsync, tick} from '@angular/core/testing'; import {fakeAsync, tick} from '@angular/core/testing';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {iterateListLike} from '../../src/facade/collection';
export function main() { export function main() {
describe('QueryList', () => { describe('QueryList', () => {
let queryList: QueryList<string>; let queryList: QueryList<string>;

View File

@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ɵisPromise as isPromise} from '@angular/core'; import {ɵisPromise as isPromise, ɵmerge as merge} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer'; import {AsyncTestCompleter} from './async_test_completer';
import {StringMapWrapper} from './facade/collection';
import {global} from './facade/lang'; import {global} from './facade/lang';
import {getTestBed, inject} from './test_bed'; import {getTestBed, inject} from './test_bed';
@ -197,7 +196,7 @@ export class SpyObject {
object = new SpyObject(); object = new SpyObject();
} }
const m = StringMapWrapper.merge(config, overrides); const m = merge(config, overrides);
Object.keys(m).forEach(key => { object.spy(key).and.returnValue(m[key]); }); Object.keys(m).forEach(key => { object.spy(key).and.returnValue(m[key]); });
return object; return object;
} }

View File

@ -1,131 +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 {getSymbolIterator, isJsObject, isPresent} from './lang';
/**
* Wraps Javascript Objects
*/
export class StringMapWrapper {
static merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
const m: {[key: string]: V} = {};
for (const k of Object.keys(m1)) {
m[k] = m1[k];
}
for (const k of Object.keys(m2)) {
m[k] = m2[k];
}
return m;
}
static equals<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean {
const k1 = Object.keys(m1);
const k2 = Object.keys(m2);
if (k1.length != k2.length) {
return false;
}
for (let i = 0; i < k1.length; i++) {
const key = k1[i];
if (m1[key] !== m2[key]) {
return false;
}
}
return true;
}
}
/**
* A boolean-valued function over a value, possibly including context information
* regarding that value's position in an array.
*/
export interface Predicate<T> { (value: T, index?: number, array?: T[]): boolean; }
export class ListWrapper {
static findLast<T>(arr: T[], condition: (value: T) => boolean): T {
for (let i = arr.length - 1; i >= 0; i--) {
if (condition(arr[i])) {
return arr[i];
}
}
return null;
}
static removeAll<T>(list: T[], items: T[]) {
for (let i = 0; i < items.length; ++i) {
const index = list.indexOf(items[i]);
if (index > -1) {
list.splice(index, 1);
}
}
}
static remove<T>(list: T[], el: T): boolean {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
return true;
}
return false;
}
static equals(a: any[], b: any[]): boolean {
if (a.length != b.length) return false;
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
static flatten<T>(list: Array<T|T[]>): T[] {
return list.reduce((flat: any[], item: T | T[]): T[] => {
const flatItem = Array.isArray(item) ? ListWrapper.flatten(item) : item;
return (<T[]>flat).concat(flatItem);
}, []);
}
}
export function isListLikeIterable(obj: any): boolean {
if (!isJsObject(obj)) return false;
return Array.isArray(obj) ||
(!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
}
export function areIterablesEqual(
a: any, b: any, comparator: (a: any, b: any) => boolean): boolean {
const iterator1 = a[getSymbolIterator()]();
const iterator2 = b[getSymbolIterator()]();
while (true) {
const item1 = iterator1.next();
const item2 = iterator2.next();
if (item1.done && item2.done) return true;
if (item1.done || item2.done) return false;
if (!comparator(item1.value, item2.value)) return false;
}
}
export function iterateListLike(obj: any, fn: (p: any) => any) {
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
fn(obj[i]);
}
} else {
const iterator = obj[getSymbolIterator()]();
let item: any;
while (!((item = iterator.next()).done)) {
fn(item.value);
}
}
}

View File

@ -1,50 +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 {StringMapWrapper} from '../src/collection';
export function main() {
describe('StringMapWrapper', () => {
describe('equals', () => {
it('should return true when comparing empty maps',
() => { expect(StringMapWrapper.equals({}, {})).toBe(true); });
it('should return true when comparing the same map', () => {
const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
expect(StringMapWrapper.equals(m1, m1)).toBe(true);
});
it('should return true when comparing different maps with the same keys and values', () => {
const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
expect(StringMapWrapper.equals(m1, m2)).toBe(true);
});
it('should return false when comparing maps with different numbers of keys', () => {
const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
expect(StringMapWrapper.equals(m1, m2)).toBe(false);
expect(StringMapWrapper.equals(m2, m1)).toBe(false);
});
it('should return false when comparing maps with different keys', () => {
const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'CC': 3};
expect(StringMapWrapper.equals(m1, m2)).toBe(false);
expect(StringMapWrapper.equals(m2, m1)).toBe(false);
});
it('should return false when comparing maps with different values', () => {
const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3};
const m2: {[key: string]: number} = {'a': 1, 'b': 20, 'c': 3};
expect(StringMapWrapper.equals(m1, m2)).toBe(false);
expect(StringMapWrapper.equals(m2, m1)).toBe(false);
});
});
});
}

View File

@ -7,8 +7,6 @@
*/ */
import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
import {ListWrapper} from '../../facade/collection';
import {FormArray, FormControl, FormGroup} from '../../model'; import {FormArray, FormControl, FormGroup} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators';
import {ControlContainer} from '../control_container'; import {ControlContainer} from '../control_container';
@ -107,7 +105,7 @@ export class FormGroupDirective extends ControlContainer implements Form,
getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); } getControl(dir: FormControlName): FormControl { return <FormControl>this.form.get(dir.path); }
removeControl(dir: FormControlName): void { ListWrapper.remove(this.directives, dir); } removeControl(dir: FormControlName): void { remove(this.directives, dir); }
addFormGroup(dir: FormGroupName): void { addFormGroup(dir: FormGroupName): void {
const ctrl: any = this.form.get(dir.path); const ctrl: any = this.form.get(dir.path);
@ -181,3 +179,10 @@ export class FormGroupDirective extends ControlContainer implements Form,
} }
} }
} }
function remove<T>(list: T[], el: T): void {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
}
}

View File

@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {InjectionToken, ɵisPromise as isPromise} from '@angular/core'; import {InjectionToken, ɵisPromise as isPromise, ɵmerge as merge} from '@angular/core';
import {toPromise} from 'rxjs/operator/toPromise'; import {toPromise} from 'rxjs/operator/toPromise';
import {AsyncValidatorFn, Validator, ValidatorFn} from './directives/validators'; import {AsyncValidatorFn, Validator, ValidatorFn} from './directives/validators';
import {StringMapWrapper} from './facade/collection';
import {isPresent} from './facade/lang'; import {isPresent} from './facade/lang';
import {AbstractControl, FormControl, FormGroup} from './model'; import {AbstractControl, FormControl, FormGroup} from './model';
@ -203,7 +202,7 @@ function _executeAsyncValidators(control: AbstractControl, validators: AsyncVali
function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} { function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} {
const res: {[key: string]: any} = const res: {[key: string]: any} =
arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => { arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => {
return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res; return isPresent(errors) ? merge(res, errors) : res;
}, {}); }, {});
return Object.keys(res).length === 0 ? null : res; return Object.keys(res).length === 0 ? null : res;
} }

View File

@ -6,10 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {DebugElement, Type} from '@angular/core'; import {DebugElement, Predicate, Type} from '@angular/core';
import {getDOM} from '../../dom/dom_adapter'; import {getDOM} from '../../dom/dom_adapter';
import {Predicate} from '../../facade/collection';
import {isPresent} from '../../facade/lang'; import {isPresent} from '../../facade/lang';

View File

@ -7,8 +7,6 @@
*/ */
import * as core from '@angular/core'; import * as core from '@angular/core';
import {StringMapWrapper} from '../../facade/collection';
import {getDOM} from '../dom_adapter'; import {getDOM} from '../dom_adapter';
const CORE_TOKENS = { const CORE_TOKENS = {
@ -40,8 +38,7 @@ export function _createNgProbe(extraTokens: NgProbeToken[], coreTokens: core.NgP
const tokens = (extraTokens || []).concat(coreTokens || []); const tokens = (extraTokens || []).concat(coreTokens || []);
getDOM().setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement); getDOM().setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement);
getDOM().setGlobalVar( getDOM().setGlobalVar(
CORE_TOKENS_GLOBAL_NAME, CORE_TOKENS_GLOBAL_NAME, core.ɵmerge(CORE_TOKENS, _ngProbeTokensToMap(tokens || [])));
StringMapWrapper.merge(CORE_TOKENS, _ngProbeTokensToMap(tokens || [])));
return () => inspectNativeElement; return () => inspectNativeElement;
} }

View File

@ -8,7 +8,6 @@
const parse5 = require('parse5'); const parse5 = require('parse5');
import {ListWrapper} from '../src/facade/collection';
import {ɵDomAdapter as DomAdapter, ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/platform-browser'; import {ɵDomAdapter as DomAdapter, ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/platform-browser';
import {isPresent, isBlank, global, setValueOnPath} from '../src/facade/lang'; import {isPresent, isBlank, global, setValueOnPath} from '../src/facade/lang';
import {SelectorMatcher, CssSelector} from '@angular/compiler'; import {SelectorMatcher, CssSelector} from '@angular/compiler';
@ -141,7 +140,7 @@ export class Parse5DomAdapter extends DomAdapter {
} }
onAndCancel(el: any, evt: any, listener: any): Function { onAndCancel(el: any, evt: any, listener: any): Function {
this.on(el, evt, listener); this.on(el, evt, listener);
return () => { ListWrapper.remove(<any[]>(el._eventListenersMap[evt]), listener); }; return () => { remove(<any[]>(el._eventListenersMap[evt]), listener); };
} }
dispatchEvent(el: any, evt: any) { dispatchEvent(el: any, evt: any) {
if (!evt.target) { if (!evt.target) {
@ -779,3 +778,10 @@ const _HTMLElementPropertyList = [
'closure_lm_714617', 'closure_lm_714617',
'__jsaction', '__jsaction',
]; ];
function remove<T>(list: T[], el: T): void {
const index = list.indexOf(el);
if (index > -1) {
list.splice(index, 1);
}
}

View File

@ -8,8 +8,6 @@
import {Injectable, RenderComponentType, Renderer, RendererFactoryV2, RendererTypeV2, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core'; import {Injectable, RenderComponentType, Renderer, RendererFactoryV2, RendererTypeV2, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core';
import {ListWrapper} from '../../facade/collection';
import {isPresent} from '../../facade/lang';
import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker'; import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker';
import {MessageBus} from '../shared/message_bus'; import {MessageBus} from '../shared/message_bus';
import {EVENT_V2_CHANNEL, RENDERER_V2_CHANNEL} from '../shared/messaging_api'; import {EVENT_V2_CHANNEL, RENDERER_V2_CHANNEL} from '../shared/messaging_api';

View File

@ -717,6 +717,11 @@ export declare abstract class PlatformRef {
abstract onDestroy(callback: () => void): void; abstract onDestroy(callback: () => void): void;
} }
/** @experimental */
export interface Predicate<T> {
(value: T, index?: number, array?: T[]): boolean;
}
/** @stable */ /** @stable */
export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[]; export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];