From fdb22bd1854d245c0422463edbd16796caaaf892 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 19 Sep 2016 17:15:57 -0700 Subject: [PATCH 01/70] refactor: misc cleanup (#11654) --- .../@angular/benchpress/src/measure_values.ts | 7 +--- .../benchpress/src/metric/user_metric.ts | 6 +-- .../test/metric/perflog_metric_spec.ts | 2 +- .../test/metric/user_metric_spec.ts | 2 +- .../compiler/src/expression_parser/ast.ts | 2 +- .../src/view_compiler/compile_element.ts | 2 +- .../testing/directive_resolver_mock.ts | 5 +-- .../testing/ng_module_resolver_mock.ts | 16 +++---- .../compiler/testing/pipe_resolver_mock.ts | 2 - .../compiler/testing/resource_loader_mock.ts | 4 +- .../core/src/animation/view_animation_map.ts | 12 +++--- .../core/src/di/reflective_injector.ts | 6 +-- .../@angular/core/src/linker/view_utils.ts | 2 +- .../@angular/core/src/reflection/reflector.ts | 11 ++--- .../core/src/testability/testability.ts | 3 +- modules/@angular/facade/src/collection.ts | 23 ++-------- .../form_control_directive.ts | 3 +- .../form_group_directive.ts | 4 +- .../@angular/forms/src/directives/shared.ts | 6 +-- modules/@angular/forms/src/model.ts | 5 +-- modules/@angular/http/src/headers.ts | 2 +- .../@angular/http/src/url_search_params.ts | 42 +++++++------------ .../http/test/backends/jsonp_backend_spec.ts | 3 -- .../http/test/backends/xhr_backend_spec.ts | 3 -- modules/@angular/http/test/headers_spec.ts | 23 ++++------ .../src/browser/browser_adapter.ts | 2 +- .../src/dom/events/hammer_common.ts | 5 +-- .../src/dom/events/key_events.ts | 2 +- .../test/dom/events/event_manager_spec.ts | 31 +++++++------- .../platform-server/src/parse5_adapter.ts | 22 ++++------ .../shared/client_message_broker.ts | 8 +--- .../web_workers/shared/post_message_bus.ts | 15 ++++--- .../shared/service_message_broker.ts | 3 +- .../src/web_workers/ui/event_serializer.ts | 5 +-- .../web_workers/worker/platform_location.ts | 3 +- .../shared/web_worker_test_util.ts | 14 +++---- tools/public_api_guard/core/index.d.ts | 2 +- 37 files changed, 112 insertions(+), 196 deletions(-) diff --git a/modules/@angular/benchpress/src/measure_values.ts b/modules/@angular/benchpress/src/measure_values.ts index c8ef430a4086..13a67238dc41 100644 --- a/modules/@angular/benchpress/src/measure_values.ts +++ b/modules/@angular/benchpress/src/measure_values.ts @@ -6,18 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {Map} from './facade/collection'; -import {Date, DateWrapper} from './facade/lang'; - export class MeasureValues { constructor( public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {} toJson() { return { - 'timeStamp': DateWrapper.toJson(this.timeStamp), + 'timeStamp': this.timeStamp.toJSON(), 'runIndex': this.runIndex, - 'values': this.values + 'values': this.values, }; } } diff --git a/modules/@angular/benchpress/src/metric/user_metric.ts b/modules/@angular/benchpress/src/metric/user_metric.ts index 05d43e5d98e1..e5828a1ebbf9 100644 --- a/modules/@angular/benchpress/src/metric/user_metric.ts +++ b/modules/@angular/benchpress/src/metric/user_metric.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core'; +import {Inject, Injectable} from '@angular/core'; import {Options} from '../common_options'; import {StringMapWrapper} from '../facade/collection'; @@ -48,9 +48,9 @@ export class UserMetric extends Metric { if (values.every(isNumber)) { Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`))) .then((_: any[]) => { - let map = StringMapWrapper.create(); + let map: {[k: string]: any} = {}; for (let i = 0, n = names.length; i < n; i++) { - StringMapWrapper.set(map, names[i], values[i]); + map[names[i]] = values[i]; } resolve(map); }, reject); diff --git a/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts b/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts index d844fdc066e9..43f1a5ea447f 100644 --- a/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts +++ b/modules/@angular/benchpress/test/metric/perflog_metric_spec.ts @@ -33,7 +33,7 @@ export function main() { new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); } if (isBlank(microMetrics)) { - microMetrics = StringMapWrapper.create(); + microMetrics = {}; } var providers: Provider[] = [ Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS, diff --git a/modules/@angular/benchpress/test/metric/user_metric_spec.ts b/modules/@angular/benchpress/test/metric/user_metric_spec.ts index 48b33db31f29..9f034c56a56c 100644 --- a/modules/@angular/benchpress/test/metric/user_metric_spec.ts +++ b/modules/@angular/benchpress/test/metric/user_metric_spec.ts @@ -24,7 +24,7 @@ export function main() { new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true}); } if (isBlank(userMetrics)) { - userMetrics = StringMapWrapper.create(); + userMetrics = {}; } wdAdapter = new MockDriverAdapter(); var providers: Provider[] = [ diff --git a/modules/@angular/compiler/src/expression_parser/ast.ts b/modules/@angular/compiler/src/expression_parser/ast.ts index 331fe62d66ef..8c65ead52fbf 100644 --- a/modules/@angular/compiler/src/expression_parser/ast.ts +++ b/modules/@angular/compiler/src/expression_parser/ast.ts @@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor { } visitAll(asts: any[]): any[] { - var res = ListWrapper.createFixedSize(asts.length); + var res = new Array(asts.length); for (var i = 0; i < asts.length; ++i) { res[i] = asts[i].visit(this); } diff --git a/modules/@angular/compiler/src/view_compiler/compile_element.ts b/modules/@angular/compiler/src/view_compiler/compile_element.ts index f394d68dd9ad..01ee5d8cffe5 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_element.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_element.ts @@ -117,7 +117,7 @@ export class CompileElement extends CompileNode { setComponentView(compViewExpr: o.Expression) { this._compViewExpr = compViewExpr; this.contentNodesByNgContentIndex = - ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length); + new Array(this.component.template.ngContentSelectors.length); for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) { this.contentNodesByNgContentIndex[i] = []; } diff --git a/modules/@angular/compiler/testing/directive_resolver_mock.ts b/modules/@angular/compiler/testing/directive_resolver_mock.ts index c2937cef007f..772538540561 100644 --- a/modules/@angular/compiler/testing/directive_resolver_mock.ts +++ b/modules/@angular/compiler/testing/directive_resolver_mock.ts @@ -8,8 +8,7 @@ import {DirectiveResolver} from '@angular/compiler'; import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef} from '@angular/core'; -import {Map} from './facade/collection'; -import {isArray, isPresent} from './facade/lang'; +import {isPresent} from './facade/lang'; import {ViewMetadata} from './private_import_core'; @@ -156,7 +155,7 @@ function flattenArray(tree: any[], out: Array|any[]>): void { if (!isPresent(tree)) return; for (var i = 0; i < tree.length; i++) { var item = resolveForwardRef(tree[i]); - if (isArray(item)) { + if (Array.isArray(item)) { flattenArray(item, out); } else { out.push(item); diff --git a/modules/@angular/compiler/testing/ng_module_resolver_mock.ts b/modules/@angular/compiler/testing/ng_module_resolver_mock.ts index 63cdbbb0e48d..7d0400e86d3b 100644 --- a/modules/@angular/compiler/testing/ng_module_resolver_mock.ts +++ b/modules/@angular/compiler/testing/ng_module_resolver_mock.ts @@ -9,18 +9,12 @@ import {NgModuleResolver} from '@angular/compiler'; import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core'; -import {Map} from './facade/collection'; - @Injectable() export class MockNgModuleResolver extends NgModuleResolver { private _ngModules = new Map, NgModule>(); constructor(private _injector: Injector) { super(); } - private get _compiler(): Compiler { return this._injector.get(Compiler); } - - private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); } - /** * Overrides the {@link NgModule} for a module. */ @@ -36,10 +30,10 @@ export class MockNgModuleResolver extends NgModuleResolver { * `NgModuleResolver`, see `setNgModule`. */ resolve(type: Type, throwIfNotFound = true): NgModule { - var metadata = this._ngModules.get(type); - if (!metadata) { - metadata = super.resolve(type, throwIfNotFound); - } - return metadata; + return this._ngModules.get(type) || super.resolve(type, throwIfNotFound); } + + private get _compiler(): Compiler { return this._injector.get(Compiler); } + + private _clearCacheFor(component: Type) { this._compiler.clearCacheFor(component); } } diff --git a/modules/@angular/compiler/testing/pipe_resolver_mock.ts b/modules/@angular/compiler/testing/pipe_resolver_mock.ts index bc103d562ee1..8bd538dacb2e 100644 --- a/modules/@angular/compiler/testing/pipe_resolver_mock.ts +++ b/modules/@angular/compiler/testing/pipe_resolver_mock.ts @@ -9,8 +9,6 @@ import {PipeResolver} from '@angular/compiler'; import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core'; -import {Map} from './facade/collection'; - @Injectable() export class MockPipeResolver extends PipeResolver { private _pipes = new Map, Pipe>(); diff --git a/modules/@angular/compiler/testing/resource_loader_mock.ts b/modules/@angular/compiler/testing/resource_loader_mock.ts index 419a6b9946e0..65ee7bf63d74 100644 --- a/modules/@angular/compiler/testing/resource_loader_mock.ts +++ b/modules/@angular/compiler/testing/resource_loader_mock.ts @@ -7,11 +7,9 @@ */ import {ResourceLoader} from '@angular/compiler'; -import {ListWrapper, Map} from './facade/collection'; +import {ListWrapper} from './facade/collection'; import {isBlank, normalizeBlank} from './facade/lang'; - - /** * A mock implementation of {@link ResourceLoader} that allows outgoing requests to be mocked * and responded to within a single test, without going to the network. diff --git a/modules/@angular/core/src/animation/view_animation_map.ts b/modules/@angular/core/src/animation/view_animation_map.ts index fd3c6e3af9df..1f013bf567ad 100644 --- a/modules/@angular/core/src/animation/view_animation_map.ts +++ b/modules/@angular/core/src/animation/view_animation_map.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ListWrapper, Map, StringMapWrapper} from '../facade/collection'; +import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {isPresent} from '../facade/lang'; import {AnimationPlayer} from './animation_player'; @@ -47,12 +47,12 @@ export class ViewAnimationMap { getAllPlayers(): AnimationPlayer[] { return this._allPlayers; } remove(element: any, animationName: string): void { - var playersByAnimation = this._map.get(element); - if (isPresent(playersByAnimation)) { - var player = playersByAnimation[animationName]; + const playersByAnimation = this._map.get(element); + if (playersByAnimation) { + const player = playersByAnimation[animationName]; delete playersByAnimation[animationName]; - var index = this._allPlayers.indexOf(player); - ListWrapper.removeAt(this._allPlayers, index); + const index = this._allPlayers.indexOf(player); + this._allPlayers.splice(index, 1); if (StringMapWrapper.isEmpty(playersByAnimation)) { this._map.delete(element); diff --git a/modules/@angular/core/src/di/reflective_injector.ts b/modules/@angular/core/src/di/reflective_injector.ts index 88bdcc134308..ef6a8a678df7 100644 --- a/modules/@angular/core/src/di/reflective_injector.ts +++ b/modules/@angular/core/src/di/reflective_injector.ts @@ -121,7 +121,7 @@ export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoIn constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) { var len = providers.length; - this.keyIds = ListWrapper.createFixedSize(len); + this.keyIds = new Array(len); for (var i = 0; i < len; i++) { this.keyIds[i] = providers[i].key.id; @@ -286,7 +286,7 @@ export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStra constructor( public protoStrategy: ReflectiveProtoInjectorDynamicStrategy, public injector: ReflectiveInjector_) { - this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length); + this.objs = new Array(protoStrategy.providers.length); ListWrapper.fill(this.objs, UNDEFINED); } @@ -648,7 +648,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector { private _instantiateProvider(provider: ResolvedReflectiveProvider): any { if (provider.multiProvider) { - var res = ListWrapper.createFixedSize(provider.resolvedFactories.length); + var res = new Array(provider.resolvedFactories.length); for (var i = 0; i < provider.resolvedFactories.length; ++i) { res[i] = this._instantiate(provider, provider.resolvedFactories[i]); } diff --git a/modules/@angular/core/src/linker/view_utils.ts b/modules/@angular/core/src/linker/view_utils.ts index ab7ee4637966..520039ab061b 100644 --- a/modules/@angular/core/src/linker/view_utils.ts +++ b/modules/@angular/core/src/linker/view_utils.ts @@ -77,7 +77,7 @@ export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: nu res = EMPTY_ARR; } else if (projectableNodes.length < expectedSlotCount) { var givenSlotCount = projectableNodes.length; - res = ListWrapper.createFixedSize(expectedSlotCount); + res = new Array(expectedSlotCount); for (var i = 0; i < expectedSlotCount; i++) { res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR; } diff --git a/modules/@angular/core/src/reflection/reflector.ts b/modules/@angular/core/src/reflection/reflector.ts index 9017dfd291b3..024c1222361e 100644 --- a/modules/@angular/core/src/reflection/reflector.ts +++ b/modules/@angular/core/src/reflection/reflector.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Map, MapWrapper, Set, SetWrapper, StringMapWrapper} from '../facade/collection'; +import {MapWrapper, SetWrapper, StringMapWrapper} from '../facade/collection'; import {isPresent} from '../facade/lang'; import {Type} from '../type'; import {PlatformReflectionCapabilities} from './platform_reflection_capabilities'; @@ -40,14 +40,9 @@ export class Reflector extends ReflectorReader { /** @internal */ _methods = new Map(); /** @internal */ - _usedKeys: Set; - reflectionCapabilities: PlatformReflectionCapabilities; + _usedKeys: Set = null; - constructor(reflectionCapabilities: PlatformReflectionCapabilities) { - super(); - this._usedKeys = null; - this.reflectionCapabilities = reflectionCapabilities; - } + constructor(public reflectionCapabilities: PlatformReflectionCapabilities) { super(); } updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; } diff --git a/modules/@angular/core/src/testability/testability.ts b/modules/@angular/core/src/testability/testability.ts index 8ef3d6726b0b..e0e86f4aa6e9 100644 --- a/modules/@angular/core/src/testability/testability.ts +++ b/modules/@angular/core/src/testability/testability.ts @@ -7,7 +7,7 @@ */ import {Injectable} from '../di'; -import {Map, MapWrapper} from '../facade/collection'; +import {MapWrapper} from '../facade/collection'; import {scheduleMicroTask} from '../facade/lang'; import {NgZone} from '../zone/ng_zone'; @@ -110,6 +110,7 @@ export class Testability implements PublicTestability { getPendingRequestCount(): number { return this._pendingCount; } + /** @deprecated use findProviders */ findBindings(using: any, provider: string, exactMatch: boolean): any[] { // TODO(juliemr): implement. return []; diff --git a/modules/@angular/facade/src/collection.ts b/modules/@angular/facade/src/collection.ts index 1308f2851899..fa4a76070671 100644 --- a/modules/@angular/facade/src/collection.ts +++ b/modules/@angular/facade/src/collection.ts @@ -8,12 +8,9 @@ import {getSymbolIterator, global, isArray, isBlank, isJsObject, isPresent} from './lang'; -export var Map = global.Map; -export var Set = global.Set; - // Safari and Internet Explorer do not support the iterable parameter to the // Map constructor. We work around that by manually adding the items. -var createMapFromPairs: {(pairs: any[]): Map} = (function() { +const createMapFromPairs: {(pairs: any[]): Map} = (function() { try { if (new Map([[1, 2]]).size === 1) { return function createMapFromPairs(pairs: any[]): Map { return new Map(pairs); }; @@ -29,7 +26,7 @@ var createMapFromPairs: {(pairs: any[]): Map} = (function() { return map; }; })(); -var createMapFromMap: {(m: Map): Map} = (function() { +const createMapFromMap: {(m: Map): Map} = (function() { try { if (new Map(new Map())) { return function createMapFromMap(m: Map): Map { return new Map(m); }; @@ -42,7 +39,7 @@ var createMapFromMap: {(m: Map): Map} = (function() { return map; }; })(); -var _clearValues: {(m: Map): void} = (function() { +const _clearValues: {(m: Map): void} = (function() { if (((new Map()).keys()).next) { return function _clearValues(m: Map) { var keyIterator = m.keys(); @@ -69,7 +66,7 @@ var _arrayFromMap: {(m: Map, getValues: boolean): any[]} = (function() } catch (e) { } return function createArrayFromMapWithForeach(m: Map, getValues: boolean): any[] { - var res = ListWrapper.createFixedSize(m.size), i = 0; + var res = new Array(m.size), i = 0; m.forEach((v, k) => { res[i] = getValues ? v : k; i++; @@ -79,7 +76,6 @@ var _arrayFromMap: {(m: Map, getValues: boolean): any[]} = (function() })(); export class MapWrapper { - static clone(m: Map): Map { return createMapFromMap(m); } static createFromStringMap(stringMap: {[key: string]: T}): Map { var result = new Map(); for (var prop in stringMap) { @@ -93,7 +89,6 @@ export class MapWrapper { return r; } static createFromPairs(pairs: any[]): Map { return createMapFromPairs(pairs); } - static clearValues(m: Map) { _clearValues(m); } static iterable(m: T): T { return m; } static keys(m: Map): K[] { return _arrayFromMap(m, false); } static values(m: Map): V[] { return _arrayFromMap(m, true); } @@ -103,15 +98,6 @@ export class MapWrapper { * Wraps Javascript Objects */ export class StringMapWrapper { - static create(): {[k: /*any*/ string]: any} { - // Note: We are not using Object.create(null) here due to - // performance! - // http://jsperf.com/ng2-object-create-null - return {}; - } - static contains(map: {[key: string]: any}, key: string): boolean { - return map.hasOwnProperty(key); - } static get(map: {[key: string]: V}, key: string): V { return map.hasOwnProperty(key) ? map[key] : undefined; } @@ -127,7 +113,6 @@ export class StringMapWrapper { } return true; } - static delete (map: {[key: string]: any}, key: string) { delete map[key]; } static forEach(map: {[key: string]: V}, callback: (v: V, K: string) => void) { for (let k of Object.keys(map)) { callback(map[k], k); diff --git a/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts index d7afecc64d68..4db811043257 100644 --- a/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts @@ -9,7 +9,6 @@ import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; import {EventEmitter} from '../../facade/async'; -import {StringMapWrapper} from '../../facade/collection'; import {FormControl} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor'; @@ -117,6 +116,6 @@ export class FormControlDirective extends NgControl implements OnChanges { } private _isControlChanged(changes: {[key: string]: any}): boolean { - return StringMapWrapper.contains(changes, 'form'); + return changes.hasOwnProperty('form'); } } diff --git a/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts index baa102c01246..ab41b561fbe8 100644 --- a/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts @@ -9,7 +9,7 @@ import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; import {EventEmitter} from '../../facade/async'; -import {ListWrapper, StringMapWrapper} from '../../facade/collection'; +import {ListWrapper} from '../../facade/collection'; import {isBlank} from '../../facade/lang'; import {FormArray, FormControl, FormGroup} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; @@ -80,7 +80,7 @@ export class FormGroupDirective extends ControlContainer implements Form, ngOnChanges(changes: SimpleChanges): void { this._checkFormPresent(); - if (StringMapWrapper.contains(changes, 'form')) { + if (changes.hasOwnProperty('form')) { this._updateValidators(); this._updateDomValue(); this._updateRegistrations(); diff --git a/modules/@angular/forms/src/directives/shared.ts b/modules/@angular/forms/src/directives/shared.ts index 2ff9d746036f..b2b87668348e 100644 --- a/modules/@angular/forms/src/directives/shared.ts +++ b/modules/@angular/forms/src/directives/shared.ts @@ -7,7 +7,7 @@ */ -import {ListWrapper, StringMapWrapper} from '../facade/collection'; +import {ListWrapper} from '../facade/collection'; import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang'; import {FormArray, FormControl, FormGroup} from '../model'; import {Validators} from '../validators'; @@ -120,8 +120,8 @@ export function composeAsyncValidators(validators: /* Array } export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean { - if (!StringMapWrapper.contains(changes, 'model')) return false; - var change = changes['model']; + if (!changes.hasOwnProperty('model')) return false; + const change = changes['model']; if (change.isFirstChange()) return true; return !looseIdentical(viewModel, change.currentValue); diff --git a/modules/@angular/forms/src/model.ts b/modules/@angular/forms/src/model.ts index a79f98faece3..7738c754bf2d 100644 --- a/modules/@angular/forms/src/model.ts +++ b/modules/@angular/forms/src/model.ts @@ -16,7 +16,6 @@ import {isBlank, isPresent, isStringMap, normalizeBool} from './facade/lang'; import {isPromise} from './private_import_core'; - /** * Indicates that a FormControl is valid, i.e. that no errors exist in the input value. */ @@ -886,7 +885,7 @@ export class FormGroup extends AbstractControl { */ removeControl(name: string): void { if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {}); - StringMapWrapper.delete(this.controls, name); + delete (this.controls[name]); this.updateValueAndValidity(); this._onCollectionChange(); } @@ -896,7 +895,7 @@ export class FormGroup extends AbstractControl { */ setControl(name: string, control: AbstractControl): void { if (this.controls[name]) this.controls[name]._registerOnCollectionChange(() => {}); - StringMapWrapper.delete(this.controls, name); + delete (this.controls[name]); if (control) this.registerControl(name, control); this.updateValueAndValidity(); this._onCollectionChange(); diff --git a/modules/@angular/http/src/headers.ts b/modules/@angular/http/src/headers.ts index f22466ee7546..1bf2a0842e92 100644 --- a/modules/@angular/http/src/headers.ts +++ b/modules/@angular/http/src/headers.ts @@ -7,7 +7,7 @@ */ -import {ListWrapper, Map, MapWrapper, StringMapWrapper, isListLikeIterable, iterateListLike} from '../src/facade/collection'; +import {ListWrapper, MapWrapper, StringMapWrapper, isListLikeIterable, iterateListLike} from '../src/facade/collection'; import {isBlank} from '../src/facade/lang'; diff --git a/modules/@angular/http/src/url_search_params.ts b/modules/@angular/http/src/url_search_params.ts index 6bda7d0800de..7703f1c871f3 100644 --- a/modules/@angular/http/src/url_search_params.ts +++ b/modules/@angular/http/src/url_search_params.ts @@ -6,9 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {ListWrapper, Map, isListLikeIterable} from '../src/facade/collection'; -import {isPresent} from '../src/facade/lang'; - function paramParser(rawParams: string = ''): Map { const map = new Map(); if (rawParams.length > 0) { @@ -97,23 +94,16 @@ export class URLSearchParams { has(param: string): boolean { return this.paramsMap.has(param); } get(param: string): string { - var storedParam = this.paramsMap.get(param); - if (isListLikeIterable(storedParam)) { - return ListWrapper.first(storedParam); - } else { - return null; - } - } + const storedParam = this.paramsMap.get(param); - getAll(param: string): string[] { - var mapParam = this.paramsMap.get(param); - return isPresent(mapParam) ? mapParam : []; + return Array.isArray(storedParam) ? storedParam[0] : null; } + getAll(param: string): string[] { return this.paramsMap.get(param) || []; } + set(param: string, val: string) { - var mapParam = this.paramsMap.get(param); - var list = isPresent(mapParam) ? mapParam : []; - ListWrapper.clear(list); + const list = this.paramsMap.get(param) || []; + list.length = 0; list.push(val); this.paramsMap.set(param, list); } @@ -126,17 +116,15 @@ export class URLSearchParams { // TODO(@caitp): document this better setAll(searchParams: URLSearchParams) { searchParams.paramsMap.forEach((value, param) => { - var mapParam = this.paramsMap.get(param); - var list = isPresent(mapParam) ? mapParam : []; - ListWrapper.clear(list); + const list = this.paramsMap.get(param) || []; + list.length = 0; list.push(value[0]); this.paramsMap.set(param, list); }); } append(param: string, val: string): void { - var mapParam = this.paramsMap.get(param); - var list = isPresent(mapParam) ? mapParam : []; + const list = this.paramsMap.get(param) || []; list.push(val); this.paramsMap.set(param, list); } @@ -150,9 +138,8 @@ export class URLSearchParams { // TODO(@caitp): document this better appendAll(searchParams: URLSearchParams) { searchParams.paramsMap.forEach((value, param) => { - var mapParam = this.paramsMap.get(param); - var list = isPresent(mapParam) ? mapParam : []; - for (var i = 0; i < value.length; ++i) { + const list = this.paramsMap.get(param) || []; + for (let i = 0; i < value.length; ++i) { list.push(value[i]); } this.paramsMap.set(param, list); @@ -169,9 +156,8 @@ export class URLSearchParams { // TODO(@caitp): document this better replaceAll(searchParams: URLSearchParams) { searchParams.paramsMap.forEach((value, param) => { - var mapParam = this.paramsMap.get(param); - var list = isPresent(mapParam) ? mapParam : []; - ListWrapper.clear(list); + const list = this.paramsMap.get(param) || []; + list.length = 0; for (var i = 0; i < value.length; ++i) { list.push(value[i]); } @@ -180,7 +166,7 @@ export class URLSearchParams { } toString(): string { - var paramsList: string[] = []; + const paramsList: string[] = []; this.paramsMap.forEach((values, k) => { values.forEach( v => paramsList.push( diff --git a/modules/@angular/http/test/backends/jsonp_backend_spec.ts b/modules/@angular/http/test/backends/jsonp_backend_spec.ts index 87cc154583dc..da07e1f62eeb 100644 --- a/modules/@angular/http/test/backends/jsonp_backend_spec.ts +++ b/modules/@angular/http/test/backends/jsonp_backend_spec.ts @@ -9,19 +9,16 @@ import {ReflectiveInjector} from '@angular/core'; import {AsyncTestCompleter, SpyObject, afterEach, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; - import {BrowserJsonp} from '../../src/backends/browser_jsonp'; import {JSONPBackend, JSONPBackend_, JSONPConnection, JSONPConnection_} from '../../src/backends/jsonp_backend'; import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options'; import {BaseResponseOptions, ResponseOptions} from '../../src/base_response_options'; import {ReadyState, RequestMethod, ResponseType} from '../../src/enums'; -import {Map} from '../../src/facade/collection'; import {isPresent} from '../../src/facade/lang'; import {Request} from '../../src/static_request'; import {Response} from '../../src/static_response'; var existingScripts: MockBrowserJsonp[] = []; -var unused: Response; class MockBrowserJsonp extends BrowserJsonp { src: string; diff --git a/modules/@angular/http/test/backends/xhr_backend_spec.ts b/modules/@angular/http/test/backends/xhr_backend_spec.ts index 216ecda5d774..78cb2bec6070 100644 --- a/modules/@angular/http/test/backends/xhr_backend_spec.ts +++ b/modules/@angular/http/test/backends/xhr_backend_spec.ts @@ -15,7 +15,6 @@ import {CookieXSRFStrategy, XHRBackend, XHRConnection} from '../../src/backends/ import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options'; import {BaseResponseOptions, ResponseOptions} from '../../src/base_response_options'; import {ResponseContentType, ResponseType} from '../../src/enums'; -import {Map} from '../../src/facade/collection'; import {Json} from '../../src/facade/lang'; import {Headers} from '../../src/headers'; import {XSRFStrategy} from '../../src/interfaces'; @@ -27,9 +26,7 @@ var abortSpy: any; var sendSpy: any; var openSpy: any; var setRequestHeaderSpy: any; -var addEventListenerSpy: any; var existingXHRs: MockBrowserXHR[] = []; -var unused: Response; class MockBrowserXHR extends BrowserXhr { abort: any; diff --git a/modules/@angular/http/test/headers_spec.ts b/modules/@angular/http/test/headers_spec.ts index ae8593a1ef23..72c415fec3fe 100644 --- a/modules/@angular/http/test/headers_spec.ts +++ b/modules/@angular/http/test/headers_spec.ts @@ -7,8 +7,6 @@ */ import {beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; - -import {Map, StringMapWrapper} from '../src/facade/collection'; import {Json} from '../src/facade/lang'; import {Headers} from '../src/headers'; @@ -24,21 +22,20 @@ export function main() { // Spec at https://tools.ietf.org/html/rfc2616 expect(firstHeaders.get('content-type')).toBe('image/jpeg'); expect(firstHeaders.get('content-Type')).toBe('image/jpeg'); - var httpHeaders = StringMapWrapper.create(); - StringMapWrapper.set(httpHeaders, 'Content-Type', 'image/jpeg'); - StringMapWrapper.set(httpHeaders, 'Accept-Charset', 'utf-8'); - StringMapWrapper.set(httpHeaders, 'X-My-Custom-Header', 'Zeke are cool'); - var secondHeaders = new Headers(httpHeaders); - var secondHeadersObj = new Headers(secondHeaders); + const httpHeaders = { + 'Content-Type': 'image/jpeg', + 'Accept-Charset': 'utf-8', + 'X-My-Custom-Header': 'Zeke are cool', + }; + const secondHeaders = new Headers(httpHeaders); + const secondHeadersObj = new Headers(secondHeaders); expect(secondHeadersObj.get('Content-Type')).toBe('image/jpeg'); }); describe('initialization', () => { it('should merge values in provided dictionary', () => { - var map = StringMapWrapper.create(); - StringMapWrapper.set(map, 'foo', 'bar'); - var headers = new Headers(map); + var headers = new Headers({'foo': 'bar'}); expect(headers.get('foo')).toBe('bar'); expect(headers.getAll('foo')).toEqual(['bar']); }); @@ -55,9 +52,7 @@ export function main() { describe('.set()', () => { it('should clear all values and re-set for the provided key', () => { - var map = StringMapWrapper.create(); - StringMapWrapper.set(map, 'foo', 'bar'); - var headers = new Headers(map); + var headers = new Headers({'foo': 'bar'}); expect(headers.get('foo')).toBe('bar'); expect(headers.getAll('foo')).toEqual(['bar']); headers.set('foo', 'baz'); diff --git a/modules/@angular/platform-browser/src/browser/browser_adapter.ts b/modules/@angular/platform-browser/src/browser/browser_adapter.ts index 4286e5b85c10..ff005f86f603 100644 --- a/modules/@angular/platform-browser/src/browser/browser_adapter.ts +++ b/modules/@angular/platform-browser/src/browser/browser_adapter.ts @@ -164,7 +164,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter { childNodes(el: any /** TODO #9100 */): Node[] { return el.childNodes; } childNodesAsList(el: any /** TODO #9100 */): any[] { var childNodes = el.childNodes; - var res = ListWrapper.createFixedSize(childNodes.length); + var res = new Array(childNodes.length); for (var i = 0; i < childNodes.length; i++) { res[i] = childNodes[i]; } diff --git a/modules/@angular/platform-browser/src/dom/events/hammer_common.ts b/modules/@angular/platform-browser/src/dom/events/hammer_common.ts index 36256a11db08..63a4019abc9e 100644 --- a/modules/@angular/platform-browser/src/dom/events/hammer_common.ts +++ b/modules/@angular/platform-browser/src/dom/events/hammer_common.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {StringMapWrapper} from '../../facade/collection'; - import {EventManagerPlugin} from './event_manager'; var _eventNames = { @@ -53,7 +51,6 @@ export class HammerGesturesPluginCommon extends EventManagerPlugin { constructor() { super(); } supports(eventName: string): boolean { - eventName = eventName.toLowerCase(); - return StringMapWrapper.contains(_eventNames, eventName); + return _eventNames.hasOwnProperty(eventName.toLowerCase()); } } diff --git a/modules/@angular/platform-browser/src/dom/events/key_events.ts b/modules/@angular/platform-browser/src/dom/events/key_events.ts index b83ef7813bf1..d044539c3058 100644 --- a/modules/@angular/platform-browser/src/dom/events/key_events.ts +++ b/modules/@angular/platform-browser/src/dom/events/key_events.ts @@ -72,7 +72,7 @@ export class KeyEventsPlugin extends EventManagerPlugin { // returning null instead of throwing to let another plugin process the event return null; } - var result = StringMapWrapper.create(); + var result = {}; StringMapWrapper.set(result, 'domEventName', domEventName); StringMapWrapper.set(result, 'fullKey', fullKey); return result; diff --git a/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts b/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts index b16d7595af42..3e8cac85dce1 100644 --- a/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts +++ b/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts @@ -11,12 +11,10 @@ import {beforeEach, ddescribe, describe, expect, iit, it, xdescribe, xit} from ' import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {DomEventsPlugin} from '@angular/platform-browser/src/dom/events/dom_events'; import {EventManager, EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager'; - -import {ListWrapper, Map} from '../../../src/facade/collection'; import {el} from '../../../testing/browser_util'; export function main() { - var domEventPlugin: any /** TODO #9100 */; + var domEventPlugin: DomEventsPlugin; describe('EventManager', () => { @@ -29,7 +27,7 @@ export function main() { var plugin = new FakeEventManagerPlugin(['click']); var manager = new EventManager([domEventPlugin, plugin], new FakeNgZone()); manager.addEventListener(element, 'click', handler); - expect(plugin._eventHandler.get('click')).toBe(handler); + expect(plugin.eventHandler['click']).toBe(handler); }); it('should delegate event bindings to the first plugin supporting the event', () => { @@ -41,10 +39,8 @@ export function main() { var manager = new EventManager([plugin2, plugin1], new FakeNgZone()); manager.addEventListener(element, 'click', clickHandler); manager.addEventListener(element, 'dblclick', dblClickHandler); - expect(plugin1._eventHandler.has('click')).toBe(false); - expect(plugin2._eventHandler.get('click')).toBe(clickHandler); - expect(plugin2._eventHandler.has('dblclick')).toBe(false); - expect(plugin1._eventHandler.get('dblclick')).toBe(dblClickHandler); + expect(plugin2.eventHandler['click']).toBe(clickHandler); + expect(plugin1.eventHandler['dblclick']).toBe(dblClickHandler); }); it('should throw when no plugin can handle the event', () => { @@ -91,22 +87,23 @@ export function main() { }); } +/** @internal */ class FakeEventManagerPlugin extends EventManagerPlugin { - /** @internal */ - _eventHandler = new Map(); - constructor(public _supports: string[]) { super(); } + eventHandler: {[event: string]: Function} = {}; + + constructor(public supportedEvents: string[]) { super(); } - supports(eventName: string): boolean { return ListWrapper.contains(this._supports, eventName); } + supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; } - addEventListener(element: any /** TODO #9100 */, eventName: string, handler: Function) { - this._eventHandler.set(eventName, handler); - return () => { this._eventHandler.delete(eventName); }; + addEventListener(element: any, eventName: string, handler: Function) { + this.eventHandler[eventName] = handler; + return () => { delete (this.eventHandler[eventName]); }; } } class FakeNgZone extends NgZone { constructor() { super({enableLongStackTrace: false}); } - run(fn: any /** TODO #9100 */) { fn(); } + run(fn: Function) { fn(); } - runOutsideAngular(fn: any /** TODO #9100 */) { return fn(); } + runOutsideAngular(fn: Function) { return fn(); } } diff --git a/modules/@angular/platform-server/src/parse5_adapter.ts b/modules/@angular/platform-server/src/parse5_adapter.ts index 59f29986ffdc..59bed8e648d2 100644 --- a/modules/@angular/platform-server/src/parse5_adapter.ts +++ b/modules/@angular/platform-server/src/parse5_adapter.ts @@ -12,8 +12,6 @@ import {ListWrapper, StringMapWrapper} from '../src/facade/collection'; import {DomAdapter, setRootDomAdapter} from './private_import_platform-browser'; import {isPresent, isBlank, global, setValueOnPath, DateWrapper} from '../src/facade/lang'; import {SelectorMatcher, CssSelector} from './private_import_compiler'; -import {Type} from '@angular/core'; -import {ResourceLoader} from '@angular/compiler'; var parser: any /** TODO #9100 */ = null; var serializer: any /** TODO #9100 */ = null; @@ -136,17 +134,13 @@ export class Parse5DomAdapter extends DomAdapter { return result; } on(el: any /** TODO #9100 */, evt: any /** TODO #9100 */, listener: any /** TODO #9100 */) { - var listenersMap: {[k: /*any*/ string]: any} = el._eventListenersMap; + var listenersMap: {[k: string]: any} = el._eventListenersMap; if (isBlank(listenersMap)) { - var listenersMap: {[k: /*any*/ string]: any} = StringMapWrapper.create(); + var listenersMap: {[k: string]: any} = {}; el._eventListenersMap = listenersMap; } - var listeners = StringMapWrapper.get(listenersMap, evt); - if (isBlank(listeners)) { - listeners = []; - } - listeners.push(listener); - StringMapWrapper.set(listenersMap, evt, listeners); + const listeners = listenersMap[evt] || []; + listenersMap[evt] = [...listeners, listener]; } onAndCancel( el: any /** TODO #9100 */, evt: any /** TODO #9100 */, @@ -209,7 +203,7 @@ export class Parse5DomAdapter extends DomAdapter { childNodes(el: any /** TODO #9100 */): Node[] { return el.childNodes; } childNodesAsList(el: any /** TODO #9100 */): any[] { var childNodes = el.childNodes; - var res = ListWrapper.createFixedSize(childNodes.length); + var res = new Array(childNodes.length); for (var i = 0; i < childNodes.length; i++) { res[i] = childNodes[i]; } @@ -489,7 +483,7 @@ export class Parse5DomAdapter extends DomAdapter { } removeAttribute(element: any /** TODO #9100 */, attribute: string) { if (attribute) { - StringMapWrapper.delete(element.attribs, attribute); + delete element.attribs[attribute]; } } removeAttributeNS(element: any /** TODO #9100 */, ns: string, name: string) { @@ -507,7 +501,7 @@ export class Parse5DomAdapter extends DomAdapter { this.appendChild(newDoc, body); StringMapWrapper.set(newDoc, 'head', head); StringMapWrapper.set(newDoc, 'body', body); - StringMapWrapper.set(newDoc, '_window', StringMapWrapper.create()); + StringMapWrapper.set(newDoc, '_window', {}); return newDoc; } defaultDoc(): Document { @@ -546,7 +540,7 @@ export class Parse5DomAdapter extends DomAdapter { var rules: any[] /** TODO #9100 */ = []; for (var i = 0; i < parsedRules.length; i++) { var parsedRule = parsedRules[i]; - var rule: {[key: string]: any} = StringMapWrapper.create(); + var rule: {[key: string]: any} = {}; StringMapWrapper.set(rule, 'cssText', css); StringMapWrapper.set(rule, 'style', {content: '', cssText: ''}); if (parsedRule.type == 'rule') { diff --git a/modules/@angular/platform-webworker/src/web_workers/shared/client_message_broker.ts b/modules/@angular/platform-webworker/src/web_workers/shared/client_message_broker.ts index df95b082537e..361edf67167e 100644 --- a/modules/@angular/platform-webworker/src/web_workers/shared/client_message_broker.ts +++ b/modules/@angular/platform-webworker/src/web_workers/shared/client_message_broker.ts @@ -156,15 +156,11 @@ class MessageData { } /** - * Returns the value from the StringMap if present. Otherwise returns null + * Returns the value if present, otherwise returns null * @internal */ _getValueIfPresent(data: {[key: string]: any}, key: string) { - if (StringMapWrapper.contains(data, key)) { - return StringMapWrapper.get(data, key); - } else { - return null; - } + return data.hasOwnProperty(key) ? data[key] : null; } } diff --git a/modules/@angular/platform-webworker/src/web_workers/shared/post_message_bus.ts b/modules/@angular/platform-webworker/src/web_workers/shared/post_message_bus.ts index 8f09547e4c43..34253eb95ef2 100644 --- a/modules/@angular/platform-webworker/src/web_workers/shared/post_message_bus.ts +++ b/modules/@angular/platform-webworker/src/web_workers/shared/post_message_bus.ts @@ -9,7 +9,6 @@ import {Injectable, NgZone} from '@angular/core'; import {EventEmitter} from '../../facade/async'; -import {StringMapWrapper} from '../../facade/collection'; import {MessageBus, MessageBusSink, MessageBusSource} from './message_bus'; @@ -22,7 +21,7 @@ export interface PostMessageTarget { export class PostMessageBusSink implements MessageBusSink { private _zone: NgZone; - private _channels: {[key: string]: _Channel} = StringMapWrapper.create(); + private _channels: {[key: string]: _Channel} = {}; private _messageBuffer: Array = []; constructor(private _postMessageTarget: PostMessageTarget) {} @@ -34,7 +33,7 @@ export class PostMessageBusSink implements MessageBusSink { } initChannel(channel: string, runInZone: boolean = true): void { - if (StringMapWrapper.contains(this._channels, channel)) { + if (this._channels.hasOwnProperty(channel)) { throw new Error(`${channel} has already been initialized`); } @@ -52,7 +51,7 @@ export class PostMessageBusSink implements MessageBusSink { } to(channel: string): EventEmitter { - if (StringMapWrapper.contains(this._channels, channel)) { + if (this._channels.hasOwnProperty(channel)) { return this._channels[channel].emitter; } else { throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); @@ -71,7 +70,7 @@ export class PostMessageBusSink implements MessageBusSink { export class PostMessageBusSource implements MessageBusSource { private _zone: NgZone; - private _channels: {[key: string]: _Channel} = StringMapWrapper.create(); + private _channels: {[key: string]: _Channel} = {}; constructor(eventTarget?: EventTarget) { if (eventTarget) { @@ -86,7 +85,7 @@ export class PostMessageBusSource implements MessageBusSource { attachToZone(zone: NgZone) { this._zone = zone; } initChannel(channel: string, runInZone: boolean = true) { - if (StringMapWrapper.contains(this._channels, channel)) { + if (this._channels.hasOwnProperty(channel)) { throw new Error(`${channel} has already been initialized`); } @@ -96,7 +95,7 @@ export class PostMessageBusSource implements MessageBusSource { } from(channel: string): EventEmitter { - if (StringMapWrapper.contains(this._channels, channel)) { + if (this._channels.hasOwnProperty(channel)) { return this._channels[channel].emitter; } else { throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); @@ -112,7 +111,7 @@ export class PostMessageBusSource implements MessageBusSource { private _handleMessage(data: any): void { var channel = data.channel; - if (StringMapWrapper.contains(this._channels, channel)) { + if (this._channels.hasOwnProperty(channel)) { var channelInfo = this._channels[channel]; if (channelInfo.runInZone) { this._zone.run(() => { channelInfo.emitter.emit(data.message); }); diff --git a/modules/@angular/platform-webworker/src/web_workers/shared/service_message_broker.ts b/modules/@angular/platform-webworker/src/web_workers/shared/service_message_broker.ts index ebbdca6ee7bb..fa118fc9e095 100644 --- a/modules/@angular/platform-webworker/src/web_workers/shared/service_message_broker.ts +++ b/modules/@angular/platform-webworker/src/web_workers/shared/service_message_broker.ts @@ -9,7 +9,6 @@ import {Injectable, Type} from '@angular/core'; import {EventEmitter} from '../../facade/async'; -import {ListWrapper, Map} from '../../facade/collection'; import {FunctionWrapper, isPresent} from '../../facade/lang'; import {MessageBus} from '../shared/message_bus'; import {Serializer} from '../shared/serializer'; @@ -72,7 +71,7 @@ export class ServiceMessageBroker_ extends ServiceMessageBroker { this._methods.set(methodName, (message: ReceivedMessage) => { var serializedArgs = message.args; let numArgs = signature === null ? 0 : signature.length; - var deserializedArgs: any[] = ListWrapper.createFixedSize(numArgs); + var deserializedArgs: any[] = new Array(numArgs); for (var i = 0; i < numArgs; i++) { var serializedArg = serializedArgs[i]; deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]); diff --git a/modules/@angular/platform-webworker/src/web_workers/ui/event_serializer.ts b/modules/@angular/platform-webworker/src/web_workers/ui/event_serializer.ts index f96fc01eafb0..3196ddf0d287 100644 --- a/modules/@angular/platform-webworker/src/web_workers/ui/event_serializer.ts +++ b/modules/@angular/platform-webworker/src/web_workers/ui/event_serializer.ts @@ -6,9 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {Set} from '../../facade/collection'; -import {isPresent} from '../../facade/lang'; - const MOUSE_EVENT_PROPERTIES = [ 'altKey', 'button', 'clientX', 'clientY', 'metaKey', 'movementX', 'movementY', 'offsetX', 'offsetY', 'region', 'screenX', 'screenY', 'shiftKey' @@ -56,7 +53,7 @@ function addTarget(e: Event, serializedEvent: {[key: string]: any}): {[key: stri if (NODES_WITH_VALUE.has((e.target).tagName.toLowerCase())) { var target = e.target; serializedEvent['target'] = {'value': target.value}; - if (isPresent(target.files)) { + if (target.files) { serializedEvent['target']['files'] = target.files; } } diff --git a/modules/@angular/platform-webworker/src/web_workers/worker/platform_location.ts b/modules/@angular/platform-webworker/src/web_workers/worker/platform_location.ts index a6d91c85eaa7..893663eca127 100644 --- a/modules/@angular/platform-webworker/src/web_workers/worker/platform_location.ts +++ b/modules/@angular/platform-webworker/src/web_workers/worker/platform_location.ts @@ -10,7 +10,6 @@ import {LocationChangeListener, PlatformLocation} from '@angular/common'; import {Injectable} from '@angular/core'; import {EventEmitter} from '../../facade/async'; -import {StringMapWrapper} from '../../facade/collection'; import {StringWrapper} from '../../facade/lang'; import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker'; import {MessageBus} from '../shared/message_bus'; @@ -37,7 +36,7 @@ export class WebWorkerPlatformLocation extends PlatformLocation { this._channelSource.subscribe({ next: (msg: {[key: string]: any}) => { var listeners: Array = null; - if (StringMapWrapper.contains(msg, 'event')) { + if (msg.hasOwnProperty('event')) { let type: string = msg['event']['type']; if (StringWrapper.equals(type, 'popstate')) { listeners = this._popStateListeners; diff --git a/modules/@angular/platform-webworker/test/web_workers/shared/web_worker_test_util.ts b/modules/@angular/platform-webworker/test/web_workers/shared/web_worker_test_util.ts index eef048e5f993..fb6e5ef72de1 100644 --- a/modules/@angular/platform-webworker/test/web_workers/shared/web_worker_test_util.ts +++ b/modules/@angular/platform-webworker/test/web_workers/shared/web_worker_test_util.ts @@ -16,8 +16,6 @@ import {SpyMessageBroker} from '../worker/spies'; import {MockEventEmitter} from './mock_event_emitter'; -var __unused: Promise; // avoid unused import when Promise union types are erased - /** * Returns two MessageBus instances that are attached to each other. * Such that whatever goes into one's sink comes out the others source. @@ -49,9 +47,9 @@ export function expectBrokerCall( expect(args.method).toEqual(methodName); if (isPresent(vals)) { expect(args.args.length).toEqual(vals.length); - ListWrapper.forEachWithIndex(vals, (v, i) => { expect(v).toEqual(args.args[i].value); }); + vals.forEach((v, i) => { expect(v).toEqual(args.args[i].value); }); } - var promise: any /** TODO #9100 */ = null; + var promise: Promise|void = null; if (isPresent(handler)) { let givenValues = args.args.map((arg) => arg.value); if (givenValues.length > 0) { @@ -81,13 +79,13 @@ export class MockMessageBusSource implements MessageBusSource { constructor(private _channels: {[key: string]: MockEventEmitter}) {} initChannel(channel: string, runInZone = true) { - if (!StringMapWrapper.contains(this._channels, channel)) { + if (!this._channels.hasOwnProperty(channel)) { this._channels[channel] = new MockEventEmitter(); } } from(channel: string): MockEventEmitter { - if (!StringMapWrapper.contains(this._channels, channel)) { + if (!this._channels.hasOwnProperty(channel)) { throw new Error(`${channel} is not set up. Did you forget to call initChannel?`); } return this._channels[channel]; @@ -100,13 +98,13 @@ export class MockMessageBusSink implements MessageBusSink { constructor(private _channels: {[key: string]: MockEventEmitter}) {} initChannel(channel: string, runInZone = true) { - if (!StringMapWrapper.contains(this._channels, channel)) { + if (!this._channels.hasOwnProperty(channel)) { this._channels[channel] = new MockEventEmitter(); } } to(channel: string): MockEventEmitter { - if (!StringMapWrapper.contains(this._channels, channel)) { + if (!this._channels.hasOwnProperty(channel)) { this._channels[channel] = new MockEventEmitter(); } return this._channels[channel]; diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index 6077840e7bdf..d798eb2daf05 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -880,7 +880,7 @@ export declare abstract class TemplateRef { export declare class Testability implements PublicTestability { constructor(_ngZone: NgZone); decreasePendingRequestCount(): number; - findBindings(using: any, provider: string, exactMatch: boolean): any[]; + /** @deprecated */ findBindings(using: any, provider: string, exactMatch: boolean): any[]; findProviders(using: any, provider: string, exactMatch: boolean): any[]; getPendingRequestCount(): number; increasePendingRequestCount(): number; From a95d65241c12b721b377a8b05a0ce66b432b769c Mon Sep 17 00:00:00 2001 From: Chuck Jazdzewski Date: Tue, 20 Sep 2016 14:54:53 -0700 Subject: [PATCH 02/70] fix(compiler): Safe property access expressions work in event bindings (#11724) --- .../src/view_compiler/expression_converter.ts | 6 +++--- .../test/linker/regression_integration_spec.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/modules/@angular/compiler/src/view_compiler/expression_converter.ts b/modules/@angular/compiler/src/view_compiler/expression_converter.ts index fd44f95452ed..5fc635c58cc6 100644 --- a/modules/@angular/compiler/src/view_compiler/expression_converter.ts +++ b/modules/@angular/compiler/src/view_compiler/expression_converter.ts @@ -340,7 +340,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { // Notice that the first guard condition is the left hand of the left most safe access node // which comes in as leftMostSafe to this routine. - let guardedExpression = this.visit(leftMostSafe.receiver, mode); + let guardedExpression = this.visit(leftMostSafe.receiver, _Mode.Expression); let temporary: o.ReadVarExpr; if (this.needsTemporary(leftMostSafe.receiver)) { // If the expression has method calls or pipes then we need to save the result into a @@ -369,7 +369,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { } // Recursively convert the node now without the guarded member access. - const access = this.visit(ast, mode); + const access = this.visit(ast, _Mode.Expression); // Remove the mapping. This is not strictly required as the converter only traverses each node // once but is safer if the conversion is changed to traverse the nodes more than once. @@ -381,7 +381,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor { } // Produce the conditional - return condition.conditional(o.literal(null), access); + return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access)); } // Given a expression of the form a?.b.c?.d.e the the left most safe node is diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index cf4575058fe7..838e0bfc98f6 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -79,6 +79,24 @@ function declareTests({useJit}: {useJit: boolean}) { expect(fixture.nativeElement).toHaveText('counting method value'); expect(MyCountingComp.calls).toBe(1); }); + + it('should evalute a conditional in a statement binding', () => { + @Component({selector: 'some-comp', template: '

'}) + class SomeComponent { + nullValue: SomeReferencedClass; + } + + class SomeReferencedClass { + click() {} + } + + expect(() => { + const fixture = TestBed.configureTestingModule({declarations: [SomeComponent]}) + .createComponent(SomeComponent); + + fixture.detectChanges(/* checkNoChanges */ false); + }).not.toThrow(); + }); }); describe('providers', () => { From d7d716d5dbf68bd7709fcdecbb2d108a51663112 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 20 Sep 2016 14:55:07 -0700 Subject: [PATCH 03/70] chore(zone.js): update to 0.6.25 (#11725) --- npm-shrinkwrap.clean.json | 2 +- npm-shrinkwrap.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.clean.json b/npm-shrinkwrap.clean.json index 39c732c834e2..49d01dec75de 100644 --- a/npm-shrinkwrap.clean.json +++ b/npm-shrinkwrap.clean.json @@ -4642,7 +4642,7 @@ } }, "zone.js": { - "version": "0.6.21" + "version": "0.6.25" } }, "name": "angular-srcs", diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e02c44f8bb22..549e18a18542 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -7414,9 +7414,9 @@ } }, "zone.js": { - "version": "0.6.21", - "from": "zone.js@0.6.21", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.6.21.tgz" + "version": "0.6.25", + "from": "zone.js@>=0.6.24 <0.7.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.6.25.tgz" } } } diff --git a/package.json b/package.json index 8f4e756af392..2a2caab06a9a 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "core-js": "^2.4.1", "reflect-metadata": "^0.1.3", "rxjs": "5.0.0-beta.12", - "zone.js": "^0.6.21" + "zone.js": "^0.6.25" }, "devDependencies": { "@types/angularjs": "^1.5.13-alpha", From 39e251eea7006f150adc5db45f757ef02bdca985 Mon Sep 17 00:00:00 2001 From: Kara Date: Tue, 20 Sep 2016 14:55:47 -0700 Subject: [PATCH 04/70] fix(forms): support unbound disabled in ngModel (#11736) --- .../@angular/forms/src/directives/ng_model.ts | 4 +- .../@angular/forms/test/directives_spec.ts | 41 ++++++++++++++++++- .../forms/test/template_integration_spec.ts | 25 +++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/modules/@angular/forms/src/directives/ng_model.ts b/modules/@angular/forms/src/directives/ng_model.ts index 5e32a25d775b..91c12295345c 100644 --- a/modules/@angular/forms/src/directives/ng_model.ts +++ b/modules/@angular/forms/src/directives/ng_model.ts @@ -200,7 +200,9 @@ export class NgModel extends NgControl implements OnChanges, private _updateDisabled(changes: SimpleChanges) { const disabledValue = changes['isDisabled'].currentValue; - const isDisabled = disabledValue != null && disabledValue != false; + + const isDisabled = + disabledValue === '' || (disabledValue && disabledValue !== 'false'); resolvedPromise.then(() => { if (isDisabled && !this.control.disabled) { diff --git a/modules/@angular/forms/test/directives_spec.ts b/modules/@angular/forms/test/directives_spec.ts index 877d1ec56b85..15286e7b95c1 100644 --- a/modules/@angular/forms/test/directives_spec.ts +++ b/modules/@angular/forms/test/directives_spec.ts @@ -485,7 +485,7 @@ export function main() { }); describe('NgModel', () => { - var ngModel: any /** TODO #9100 */; + let ngModel: NgModel; beforeEach(() => { ngModel = new NgModel( @@ -539,6 +539,45 @@ export function main() { expect(ngModel.control.errors).toEqual({'async': true}); })); + + it('should mark as disabled properly', fakeAsync(() => { + ngModel.ngOnChanges({isDisabled: new SimpleChange('', undefined)}); + tick(); + expect(ngModel.control.disabled).toEqual(false); + + ngModel.ngOnChanges({isDisabled: new SimpleChange('', null)}); + tick(); + expect(ngModel.control.disabled).toEqual(false); + + ngModel.ngOnChanges({isDisabled: new SimpleChange('', false)}); + tick(); + expect(ngModel.control.disabled).toEqual(false); + + ngModel.ngOnChanges({isDisabled: new SimpleChange('', 'false')}); + tick(); + expect(ngModel.control.disabled).toEqual(false); + + ngModel.ngOnChanges({isDisabled: new SimpleChange('', 0)}); + tick(); + expect(ngModel.control.disabled).toEqual(false); + + ngModel.ngOnChanges({isDisabled: new SimpleChange(null, '')}); + tick(); + expect(ngModel.control.disabled).toEqual(true); + + ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'true')}); + tick(); + expect(ngModel.control.disabled).toEqual(true); + + ngModel.ngOnChanges({isDisabled: new SimpleChange(null, true)}); + tick(); + expect(ngModel.control.disabled).toEqual(true); + + ngModel.ngOnChanges({isDisabled: new SimpleChange(null, 'anything else')}); + tick(); + expect(ngModel.control.disabled).toEqual(true); + + })); }); describe('FormControlName', () => { diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index 0af59cbfc090..271578159dc1 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -420,6 +420,31 @@ export function main() { }); })); + it('should disable a control with unbound disabled attr', fakeAsync(() => { + TestBed.overrideComponent(NgModelForm, { + set: { + template: ` +
+ +
+ `, + } + }); + const fixture = TestBed.createComponent(NgModelForm); + fixture.detectChanges(); + tick(); + const form = fixture.debugElement.children[0].injector.get(NgForm); + expect(form.control.get('name').disabled).toBe(true); + + const input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.disabled).toEqual(true); + + form.control.enable(); + fixture.detectChanges(); + tick(); + expect(input.nativeElement.disabled).toEqual(false); + })); + }); describe('radio controls', () => { From 2860418a3cb9b16f88e1bc37ff1df036fe9cc141 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 20 Sep 2016 09:08:12 -0700 Subject: [PATCH 05/70] fix(forms): disable all radios with disable() --- modules/@angular/forms/src/model.ts | 12 +++-- .../forms/test/reactive_integration_spec.ts | 48 +++++++++++++++++++ .../forms/test/template_integration_spec.ts | 35 +++++++++++++- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/modules/@angular/forms/src/model.ts b/modules/@angular/forms/src/model.ts index 7738c754bf2d..5a9ac3bd0819 100644 --- a/modules/@angular/forms/src/model.ts +++ b/modules/@angular/forms/src/model.ts @@ -334,7 +334,7 @@ export abstract class AbstractControl { } this._updateAncestors(onlySelf); - this._onDisabledChange(true); + this._onDisabledChange.forEach((changeFn) => changeFn(true)); } /** @@ -350,7 +350,7 @@ export abstract class AbstractControl { this.updateValueAndValidity({onlySelf: true, emitEvent: emitEvent}); this._updateAncestors(onlySelf); - this._onDisabledChange(false); + this._onDisabledChange.forEach((changeFn) => changeFn(false)); } private _updateAncestors(onlySelf: boolean) { @@ -595,7 +595,7 @@ export abstract class AbstractControl { } /** @internal */ - _onDisabledChange(isDisabled: boolean): void {} + _onDisabledChange: Function[] = []; /** @internal */ _isBoxedValue(formState: any): boolean { @@ -770,14 +770,16 @@ export class FormControl extends AbstractControl { */ _clearChangeFns(): void { this._onChange = []; - this._onDisabledChange = null; + this._onDisabledChange = []; this._onCollectionChange = () => {}; } /** * Register a listener for disabled events. */ - registerOnDisabledChange(fn: (isDisabled: boolean) => void): void { this._onDisabledChange = fn; } + registerOnDisabledChange(fn: (isDisabled: boolean) => void): void { + this._onDisabledChange.push(fn); + } /** * @internal diff --git a/modules/@angular/forms/test/reactive_integration_spec.ts b/modules/@angular/forms/test/reactive_integration_spec.ts index c763d04c64c9..d4763e0f39f8 100644 --- a/modules/@angular/forms/test/reactive_integration_spec.ts +++ b/modules/@angular/forms/test/reactive_integration_spec.ts @@ -1022,6 +1022,54 @@ export function main() { }); + it('should disable all radio buttons when disable() is called', () => { + const fixture = TestBed.createComponent(FormControlRadioButtons); + const form = + new FormGroup({food: new FormControl('fish'), drink: new FormControl('cola')}); + fixture.componentInstance.form = form; + fixture.detectChanges(); + + const inputs = fixture.debugElement.queryAll(By.css('input')); + expect(inputs[0].nativeElement.disabled).toEqual(false); + expect(inputs[1].nativeElement.disabled).toEqual(false); + expect(inputs[2].nativeElement.disabled).toEqual(false); + expect(inputs[3].nativeElement.disabled).toEqual(false); + + form.get('food').disable(); + expect(inputs[0].nativeElement.disabled).toEqual(true); + expect(inputs[1].nativeElement.disabled).toEqual(true); + expect(inputs[2].nativeElement.disabled).toEqual(false); + expect(inputs[3].nativeElement.disabled).toEqual(false); + + form.disable(); + expect(inputs[0].nativeElement.disabled).toEqual(true); + expect(inputs[1].nativeElement.disabled).toEqual(true); + expect(inputs[2].nativeElement.disabled).toEqual(true); + expect(inputs[3].nativeElement.disabled).toEqual(true); + + form.enable(); + expect(inputs[0].nativeElement.disabled).toEqual(false); + expect(inputs[1].nativeElement.disabled).toEqual(false); + expect(inputs[2].nativeElement.disabled).toEqual(false); + expect(inputs[3].nativeElement.disabled).toEqual(false); + }); + + it('should disable all radio buttons when initially disabled', () => { + const fixture = TestBed.createComponent(FormControlRadioButtons); + const form = new FormGroup({ + food: new FormControl({value: 'fish', disabled: true}), + drink: new FormControl('cola') + }); + fixture.componentInstance.form = form; + fixture.detectChanges(); + + const inputs = fixture.debugElement.queryAll(By.css('input')); + expect(inputs[0].nativeElement.disabled).toEqual(true); + expect(inputs[1].nativeElement.disabled).toEqual(true); + expect(inputs[2].nativeElement.disabled).toEqual(false); + expect(inputs[3].nativeElement.disabled).toEqual(false); + }); + }); describe('custom value accessors', () => { diff --git a/modules/@angular/forms/test/template_integration_spec.ts b/modules/@angular/forms/test/template_integration_spec.ts index 271578159dc1..90d7f78bfcf0 100644 --- a/modules/@angular/forms/test/template_integration_spec.ts +++ b/modules/@angular/forms/test/template_integration_spec.ts @@ -445,6 +445,39 @@ export function main() { expect(input.nativeElement.disabled).toEqual(false); })); + it('should disable radio controls properly with programmatic call', fakeAsync(() => { + const fixture = TestBed.createComponent(NgModelRadioForm); + fixture.componentInstance.food = 'fish'; + fixture.detectChanges(); + tick(); + + const form = fixture.debugElement.children[0].injector.get(NgForm); + form.control.get('food').disable(); + tick(); + + const inputs = fixture.debugElement.queryAll(By.css('input')); + expect(inputs[0].nativeElement.disabled).toBe(true); + expect(inputs[1].nativeElement.disabled).toBe(true); + expect(inputs[2].nativeElement.disabled).toBe(false); + expect(inputs[3].nativeElement.disabled).toBe(false); + + form.control.disable(); + tick(); + + expect(inputs[0].nativeElement.disabled).toBe(true); + expect(inputs[1].nativeElement.disabled).toBe(true); + expect(inputs[2].nativeElement.disabled).toBe(true); + expect(inputs[3].nativeElement.disabled).toBe(true); + + form.control.enable(); + tick(); + + expect(inputs[0].nativeElement.disabled).toBe(false); + expect(inputs[1].nativeElement.disabled).toBe(false); + expect(inputs[2].nativeElement.disabled).toBe(false); + expect(inputs[3].nativeElement.disabled).toBe(false); + })); + }); describe('radio controls', () => { @@ -928,7 +961,7 @@ class NgModelOptionsStandalone {
- +
From 20addf5f9fff5d17cb04bbf89b4282b857107001 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Fri, 23 Sep 2016 02:29:12 +0900 Subject: [PATCH 06/70] chore(ISSUE_TEMPLATE): update Angular version field (#11821) --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 202ffbdec415..86c4d6d90b4a 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -20,7 +20,7 @@ **Please tell us about your environment:** -* **Angular version:** 2.0.0-rc.X +* **Angular version:** 2.0.X * **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ] From af6b219f8e91ed8c582eb1cc9ba3f86f3c3d68d0 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 22 Sep 2016 10:31:18 -0700 Subject: [PATCH 07/70] refactor(TemplateParser): clearer error message for on* binding (#11802) fixes #11756 --- .../src/template_parser/template_parser.ts | 25 +++++-- .../test/linker/security_integration_spec.ts | 74 +++++++++++-------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/modules/@angular/compiler/src/template_parser/template_parser.ts b/modules/@angular/compiler/src/template_parser/template_parser.ts index 01fb272b1041..776ba51ae684 100644 --- a/modules/@angular/compiler/src/template_parser/template_parser.ts +++ b/modules/@angular/compiler/src/template_parser/template_parser.ts @@ -854,7 +854,7 @@ class TemplateParseVisitor implements html.Visitor { boundPropertyName = this._schemaRegistry.getMappedPropName(partValue); securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName); bindingType = PropertyBindingType.Property; - this._assertNoEventBinding(boundPropertyName, sourceSpan); + this._assertNoEventBinding(boundPropertyName, sourceSpan, false); if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) { let errorMsg = `Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`; @@ -869,7 +869,7 @@ class TemplateParseVisitor implements html.Visitor { } else { if (parts[0] == ATTRIBUTE_PREFIX) { boundPropertyName = parts[1]; - this._assertNoEventBinding(boundPropertyName, sourceSpan); + this._assertNoEventBinding(boundPropertyName, sourceSpan, true); // NB: For security purposes, use the mapped property name, not the attribute name. const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName); securityContext = this._schemaRegistry.securityContext(elementName, mapPropName); @@ -902,12 +902,23 @@ class TemplateParseVisitor implements html.Visitor { boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan); } - private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void { + /** + * @param propName the name of the property / attribute + * @param sourceSpan + * @param isAttr true when binding to an attribute + * @private + */ + private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean): + void { if (propName.toLowerCase().startsWith('on')) { - this._reportError( - `Binding to event attribute '${propName}' is disallowed ` + - `for security reasons, please use (${propName.slice(2)})=...`, - sourceSpan, ParseErrorLevel.FATAL); + let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` + + `please use (${propName.slice(2)})=...`; + if (!isAttr) { + msg += + `\nIf '${propName}' is a directive input, make sure the directive is imported by the` + + ` current module.`; + } + this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL); } } diff --git a/modules/@angular/core/test/linker/security_integration_spec.ts b/modules/@angular/core/test/linker/security_integration_spec.ts index b0f302d3fa74..25a298237871 100644 --- a/modules/@angular/core/test/linker/security_integration_spec.ts +++ b/modules/@angular/core/test/linker/security_integration_spec.ts @@ -6,9 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core'; -import {Component} from '@angular/core/src/metadata'; -import {TestBed, getTestBed} from '@angular/core/testing'; +import {Component, Directive, Input, NO_ERRORS_SCHEMA} from '@angular/core'; +import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service'; @@ -24,12 +23,22 @@ class SecuredComponent { ctxProp: any = 'some value'; } +@Directive({selector: '[onPrefixedProp]'}) +class OnPrefixDir { + @Input() onPrefixedProp: any; + @Input() onclick: any; +} + function declareTests({useJit}: {useJit: boolean}) { describe('security integration tests', function() { beforeEach(() => { - TestBed.configureCompiler({useJit: useJit}); - TestBed.configureTestingModule({declarations: [SecuredComponent]}); + TestBed.configureCompiler({useJit: useJit}).configureTestingModule({ + declarations: [ + SecuredComponent, + OnPrefixDir, + ] + }); }); let originalLog: (msg: any) => any; @@ -43,15 +52,10 @@ function declareTests({useJit}: {useJit: boolean}) { it('should disallow binding to attr.on*', () => { const template = `
`; TestBed.overrideComponent(SecuredComponent, {set: {template}}); - try { - TestBed.createComponent(SecuredComponent); - throw 'Should throw'; - } catch (e) { - expect(e.message).toContain( - `Template parse errors:\n` + - `Binding to event attribute 'onclick' is disallowed ` + - `for security reasons, please use (click)=... `); - } + + expect(() => TestBed.createComponent(SecuredComponent)) + .toThrowError( + /Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../); }); it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => { @@ -59,17 +63,31 @@ function declareTests({useJit}: {useJit: boolean}) { TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({ schemas: [NO_ERRORS_SCHEMA] }); - ; - try { - TestBed.createComponent(SecuredComponent); - throw 'Should throw'; - } catch (e) { - expect(e.message).toContain( - `Template parse errors:\n` + - `Binding to event attribute 'onclick' is disallowed ` + - `for security reasons, please use (click)=... `); - } + + expect(() => TestBed.createComponent(SecuredComponent)) + .toThrowError( + /Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../); + }); + + it('should disallow binding to on* unless it is consumed by a directive', () => { + const template = `
`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({ + schemas: [NO_ERRORS_SCHEMA] + }); + + // should not throw for inputs starting with "on" + let cmp: ComponentFixture; + expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow(); + + // must bind to the directive not to the property of the div + const value = cmp.componentInstance.ctxProp = {}; + cmp.detectChanges(); + const div = cmp.debugElement.children[0]; + expect(div.injector.get(OnPrefixDir).onclick).toBe(value); + expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value); + expect(getDOM().hasAttribute(div.nativeElement, 'onclick')).toEqual(false); }); + }); describe('safe HTML values', function() { @@ -157,12 +175,8 @@ function declareTests({useJit}: {useJit: boolean}) { const template = `Text`; TestBed.overrideComponent(SecuredComponent, {set: {template}}); - try { - TestBed.createComponent(SecuredComponent); - throw 'Should throw'; - } catch (e) { - expect(e.message).toContain(`Can't bind to 'xlink:href'`); - } + expect(() => TestBed.createComponent(SecuredComponent)) + .toThrowError(/Can't bind to 'xlink:href'/); }); it('should escape unsafe HTML values', () => { From f218e240d3088811095505905753bc37d989980f Mon Sep 17 00:00:00 2001 From: Marc Laval Date: Thu, 22 Sep 2016 19:31:38 +0200 Subject: [PATCH 08/70] ci(BrowserStack): add Safari 10 (#11796) --- browser-providers.conf.js | 42 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/browser-providers.conf.js b/browser-providers.conf.js index 5143e9adca6e..6107380903bd 100644 --- a/browser-providers.conf.js +++ b/browser-providers.conf.js @@ -22,9 +22,11 @@ var CIconfiguration = { 'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, 'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, 'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'Safari10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, 'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}}, 'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, 'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, + 'iOS10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}, 'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}} }; @@ -83,6 +85,12 @@ var customLaunchers = { platform: 'OS X 10.11', version: '9.0' }, + 'SL_SAFARI10': { + base: 'SauceLabs', + browserName: 'safari', + platform: 'OS X 10.12', + version: '10.0' + }, 'SL_IOS7': { base: 'SauceLabs', browserName: 'iphone', @@ -101,6 +109,12 @@ var customLaunchers = { platform: 'OS X 10.10', version: '9.3' }, + 'SL_IOS10': { + base: 'SauceLabs', + browserName: 'iphone', + platform: 'OS X 10.10', + version: '10.0' + }, 'SL_IE9': { base: 'SauceLabs', browserName: 'internet explorer', @@ -186,6 +200,12 @@ var customLaunchers = { os: 'OS X', os_version: 'El Capitan' }, + 'BS_SAFARI10': { + base: 'BrowserStack', + browser: 'safari', + os: 'OS X', + os_version: 'Sierra' + }, 'BS_IOS7': { base: 'BrowserStack', device: 'iPhone 5S', @@ -204,6 +224,12 @@ var customLaunchers = { os: 'ios', os_version: '9.1' }, + 'BS_IOS10': { + base: 'BrowserStack', + device: 'iPhone SE', + os: 'ios', + os_version: '10.0' + }, 'BS_IE9': { base: 'BrowserStack', browser: 'ie', @@ -271,12 +297,12 @@ var customLaunchers = { var sauceAliases = { 'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}), - 'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], - 'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'], + 'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'], + 'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'], 'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'], 'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'], - 'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'], - 'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'], + 'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'], + 'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'], 'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'], 'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'], 'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true), @@ -285,12 +311,12 @@ var sauceAliases = { var browserstackAliases = { 'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}), - 'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], - 'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'], + 'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'], + 'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'], 'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'], 'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'], - 'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'], - 'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'], + 'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'], + 'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'], 'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true), 'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false) }; From 51e2b9c0733ce9dd1a176aaffad473c270f65c36 Mon Sep 17 00:00:00 2001 From: Stephen Fluin Date: Thu, 22 Sep 2016 10:31:56 -0700 Subject: [PATCH 09/70] docs(contributing): remove preview references (#11795) --- CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca3f837068ad..cadac6f60bd3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con ## Got a Question or Problem? If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group] -discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter]. +discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter]. ## Found an Issue? If you find a bug in the source code, you can help us by @@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by ## Want a Feature? You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub Repository][github]. If you would like to *implement* a new feature, please submit an issue with -a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview -and we are not ready to accept major contributions ahead of the full release. +a proposal for your work first, to be sure that we can use it. Please consider what kind of change it is: * For a **Major Feature**, first open an issue and outline your proposal so that it can be From 0e18c57a17df3b8891314728cf1fdb6470cceef7 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Thu, 22 Sep 2016 10:32:17 -0700 Subject: [PATCH 10/70] docs(core): mark TestBed as stable api and add preliminary docs (#11767) TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable. --- modules/@angular/core/testing/test_bed.ts | 8 +++++++- tools/public_api_guard/core/testing/index.d.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/@angular/core/testing/test_bed.ts b/modules/@angular/core/testing/test_bed.ts index 05b70271fac6..e519228f8ebc 100644 --- a/modules/@angular/core/testing/test_bed.ts +++ b/modules/@angular/core/testing/test_bed.ts @@ -47,7 +47,13 @@ export type TestModuleMetadata = { }; /** - * @experimental + * @whatItDoes Configures and initializes environment for unit testing and provides methods for + * creating components and services in unit tests. + * @description + * + * TestBed is the primary api for writing unit tests for Angular applications and libraries. + * + * @stable */ export class TestBed implements Injector { /** diff --git a/tools/public_api_guard/core/testing/index.d.ts b/tools/public_api_guard/core/testing/index.d.ts index 48d525558363..3dbd951a7423 100644 --- a/tools/public_api_guard/core/testing/index.d.ts +++ b/tools/public_api_guard/core/testing/index.d.ts @@ -56,7 +56,7 @@ export declare type MetadataOverride = { /** @experimental */ export declare function resetFakeAsyncZone(): void; -/** @experimental */ +/** @stable */ export declare class TestBed implements Injector { ngModule: Type; platform: PlatformRef; From e09882180e055d2954a55fa50314e9655e0253ef Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 22 Sep 2016 10:34:00 -0700 Subject: [PATCH 11/70] refactor(common): cleanup (#11668) --- .../@angular/common/src/directives/ng_plural.ts | 9 +++------ .../@angular/common/src/directives/ng_style.ts | 6 ++---- .../@angular/common/src/directives/ng_switch.ts | 16 +++++----------- modules/@angular/common/src/localization.ts | 2 +- modules/@angular/common/src/location/location.ts | 12 ++++++++---- modules/@angular/common/testing/location_mock.ts | 2 -- modules/@angular/facade/src/errors.ts | 11 ++--------- 7 files changed, 21 insertions(+), 37 deletions(-) diff --git a/modules/@angular/common/src/directives/ng_plural.ts b/modules/@angular/common/src/directives/ng_plural.ts index a34919570f64..0f4aa616e30b 100644 --- a/modules/@angular/common/src/directives/ng_plural.ts +++ b/modules/@angular/common/src/directives/ng_plural.ts @@ -61,8 +61,7 @@ export class NgPlural { addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; } - /** @internal */ - _updateView(): void { + private _updateView(): void { this._clearViews(); const cases = Object.keys(this._caseViews); @@ -70,13 +69,11 @@ export class NgPlural { this._activateView(this._caseViews[key]); } - /** @internal */ - _clearViews() { + private _clearViews() { if (this._activeView) this._activeView.destroy(); } - /** @internal */ - _activateView(view: SwitchView) { + private _activateView(view: SwitchView) { if (view) { this._activeView = view; this._activeView.create(); diff --git a/modules/@angular/common/src/directives/ng_style.ts b/modules/@angular/common/src/directives/ng_style.ts index dcedef2f4f59..a91f19051e7c 100644 --- a/modules/@angular/common/src/directives/ng_style.ts +++ b/modules/@angular/common/src/directives/ng_style.ts @@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif */ @Directive({selector: '[ngStyle]'}) export class NgStyle implements DoCheck { - /** @internal */ - _ngStyle: {[key: string]: string}; - /** @internal */ - _differ: KeyValueDiffer; + private _ngStyle: {[key: string]: string}; + private _differ: KeyValueDiffer; constructor( private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {} diff --git a/modules/@angular/common/src/directives/ng_switch.ts b/modules/@angular/common/src/directives/ng_switch.ts index 05a4d8bdee94..32b3254d05a1 100644 --- a/modules/@angular/common/src/directives/ng_switch.ts +++ b/modules/@angular/common/src/directives/ng_switch.ts @@ -111,8 +111,7 @@ export class NgSwitch { } } - /** @internal */ - _emptyAllActiveViews(): void { + private _emptyAllActiveViews(): void { const activeContainers = this._activeViews; for (var i = 0; i < activeContainers.length; i++) { activeContainers[i].destroy(); @@ -120,9 +119,7 @@ export class NgSwitch { this._activeViews = []; } - /** @internal */ - _activateViews(views: SwitchView[]): void { - // TODO(vicb): assert(this._activeViews.length === 0); + private _activateViews(views: SwitchView[]): void { if (views) { for (var i = 0; i < views.length; i++) { views[i].create(); @@ -141,8 +138,7 @@ export class NgSwitch { views.push(view); } - /** @internal */ - _deregisterView(value: any, view: SwitchView): void { + private _deregisterView(value: any, view: SwitchView): void { // `_CASE_DEFAULT` is used a marker for non-registered cases if (value === _CASE_DEFAULT) return; const views = this._valueViews.get(value); @@ -181,10 +177,8 @@ export class NgSwitch { @Directive({selector: '[ngSwitchCase]'}) export class NgSwitchCase { // `_CASE_DEFAULT` is used as a marker for a not yet initialized value - /** @internal */ - _value: any = _CASE_DEFAULT; - /** @internal */ - _view: SwitchView; + private _value: any = _CASE_DEFAULT; + private _view: SwitchView; private _switch: NgSwitch; constructor( diff --git a/modules/@angular/common/src/localization.ts b/modules/@angular/common/src/localization.ts index a84e73442a6b..a6c64c6fa2ef 100644 --- a/modules/@angular/common/src/localization.ts +++ b/modules/@angular/common/src/localization.ts @@ -67,7 +67,7 @@ export enum Plural { Two, Few, Many, - Other + Other, } /** diff --git a/modules/@angular/common/src/location/location.ts b/modules/@angular/common/src/location/location.ts index 8ff9dc494c2f..dab13b008745 100644 --- a/modules/@angular/common/src/location/location.ts +++ b/modules/@angular/common/src/location/location.ts @@ -49,16 +49,20 @@ export class Location { _subject: EventEmitter = new EventEmitter(); /** @internal */ _baseHref: string; - /** @internal */ _platformStrategy: LocationStrategy; constructor(platformStrategy: LocationStrategy) { this._platformStrategy = platformStrategy; - var browserBaseHref = this._platformStrategy.getBaseHref(); + const browserBaseHref = this._platformStrategy.getBaseHref(); this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref)); - this._platformStrategy.onPopState( - (ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); }); + this._platformStrategy.onPopState((ev) => { + this._subject.emit({ + 'url': this.path(true), + 'pop': true, + 'type': ev.type, + }); + }); } /** diff --git a/modules/@angular/common/testing/location_mock.ts b/modules/@angular/common/testing/location_mock.ts index 7b448bf44269..5b4d0c95908b 100644 --- a/modules/@angular/common/testing/location_mock.ts +++ b/modules/@angular/common/testing/location_mock.ts @@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core'; @Injectable() export class SpyLocation implements Location { urlChanges: string[] = []; - /** @internal */ private _history: LocationState[] = [new LocationState('', '')]; - /** @internal */ private _historyIndex: number = 0; /** @internal */ _subject: EventEmitter = new EventEmitter(); diff --git a/modules/@angular/facade/src/errors.ts b/modules/@angular/facade/src/errors.ts index bafd4e7182be..e45d83777eea 100644 --- a/modules/@angular/facade/src/errors.ts +++ b/modules/@angular/facade/src/errors.ts @@ -14,15 +14,13 @@ export function unimplemented(): any { * @stable */ export class BaseError extends Error { - /** - * @internal - */ + /** @internal **/ _nativeError: Error; constructor(message: string) { // Errors don't use current this, instead they create a new instance. // We have to do forward all of our api to the nativeInstance. - var nativeError = super(message) as any as Error; + const nativeError = super(message) as any as Error; this._nativeError = nativeError; } @@ -40,11 +38,6 @@ export class BaseError extends Error { export class WrappedError extends BaseError { originalError: any; - /** - * @internal - */ - _nativeError: Error; - constructor(message: string, error: any) { super(`${message} caused by: ${error instanceof Error ? error.message: error }`); this.originalError = error; From 3e780c032e6dd634dfba414d9db9a1d3f947f016 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 20 Sep 2016 14:14:57 -0700 Subject: [PATCH 12/70] refactor: misc cleanup --- .../compiler/src/directive_normalizer.ts | 23 ++++----- .../@angular/core/src/application_module.ts | 1 - modules/@angular/core/src/application_ref.ts | 50 +++++++++--------- .../@angular/core/src/application_tokens.ts | 2 +- .../@angular/core/src/linker/view_utils.ts | 1 - modules/@angular/core/src/metadata.ts | 2 - modules/@angular/core/src/metadata/view.ts | 51 ++++--------------- .../core/src/platform_core_providers.ts | 9 ++-- modules/@angular/core/src/type.ts | 2 +- tools/public_api_guard/core/index.d.ts | 2 +- 10 files changed, 55 insertions(+), 88 deletions(-) diff --git a/modules/@angular/compiler/src/directive_normalizer.ts b/modules/@angular/compiler/src/directive_normalizer.ts index a8e067d89cc7..729b55f37288 100644 --- a/modules/@angular/compiler/src/directive_normalizer.ts +++ b/modules/@angular/compiler/src/directive_normalizer.ts @@ -115,27 +115,27 @@ export class DirectiveNormalizer { const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata( {styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl})); - const allStyles = templateMetadataStyles.styles.concat(templateStyles.styles); - const allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls); - let encapsulation = templateMeta.encapsulation; if (isBlank(encapsulation)) { encapsulation = this._config.defaultEncapsulation; } - if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 && - allStyleUrls.length === 0) { + + const styles = templateMetadataStyles.styles.concat(templateStyles.styles); + const styleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls); + + if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 && + styleUrls.length === 0) { encapsulation = ViewEncapsulation.None; } + return new CompileTemplateMetadata({ encapsulation, - template: template, - templateUrl: templateAbsUrl, - styles: allStyles, - styleUrls: allStyleUrls, + template, + templateUrl: templateAbsUrl, styles, styleUrls, externalStylesheets: templateMeta.externalStylesheets, ngContentSelectors: visitor.ngContentSelectors, animations: templateMeta.animations, - interpolation: templateMeta.interpolation + interpolation: templateMeta.interpolation, }); } @@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate( viewProviders: directive.viewProviders, queries: directive.queries, viewQueries: directive.viewQueries, - entryComponents: directive.entryComponents, - template: template + entryComponents: directive.entryComponents, template, }); } diff --git a/modules/@angular/core/src/application_module.ts b/modules/@angular/core/src/application_module.ts index 123b716043e4..507e105b04fb 100644 --- a/modules/@angular/core/src/application_module.ts +++ b/modules/@angular/core/src/application_module.ts @@ -14,7 +14,6 @@ import {LOCALE_ID} from './i18n/tokens'; import {Compiler} from './linker/compiler'; import {ViewUtils} from './linker/view_utils'; import {NgModule} from './metadata'; -import {Type} from './type'; export function _iterableDiffersFactory() { return defaultIterableDiffers; diff --git a/modules/@angular/core/src/application_ref.ts b/modules/@angular/core/src/application_ref.ts index c1ac48631db2..8a62db492819 100644 --- a/modules/@angular/core/src/application_ref.ts +++ b/modules/@angular/core/src/application_ref.ts @@ -9,7 +9,7 @@ import {ErrorHandler} from '../src/error_handler'; import {ListWrapper} from '../src/facade/collection'; import {unimplemented} from '../src/facade/errors'; -import {isBlank, isPresent, stringify} from '../src/facade/lang'; +import {stringify} from '../src/facade/lang'; import {isPromise} from '../src/util/lang'; import {ApplicationInitStatus} from './application_init'; @@ -26,9 +26,9 @@ import {Testability, TestabilityRegistry} from './testability/testability'; import {Type} from './type'; import {NgZone} from './zone/ng_zone'; -var _devMode: boolean = true; -var _runModeLocked: boolean = false; -var _platform: PlatformRef; +let _devMode: boolean = true; +let _runModeLocked: boolean = false; +let _platform: PlatformRef; /** * Disable Angular's development mode, which turns off assertions and other @@ -67,13 +67,13 @@ export function isDevMode(): boolean { * @experimental APIs related to application bootstrap are currently under review. */ export function createPlatform(injector: Injector): PlatformRef { - if (isPresent(_platform) && !_platform.destroyed) { + if (_platform && !_platform.destroyed) { throw new Error( 'There can be only one platform. Destroy the previous one to create a new one.'); } _platform = injector.get(PlatformRef); const inits: Function[] = injector.get(PLATFORM_INITIALIZER, null); - if (isPresent(inits)) inits.forEach(init => init()); + if (inits) inits.forEach(init => init()); return _platform; } @@ -107,14 +107,17 @@ export function createPlatformFactory( * @experimental APIs related to application bootstrap are currently under review. */ export function assertPlatform(requiredToken: any): PlatformRef { - var platform = getPlatform(); - if (isBlank(platform)) { + const platform = getPlatform(); + + if (!platform) { throw new Error('No platform exists!'); } - if (isPresent(platform) && isBlank(platform.injector.get(requiredToken, null))) { + + if (!platform.injector.get(requiredToken, null)) { throw new Error( 'A platform with a different configuration has been created. Please destroy it first.'); } + return platform; } @@ -124,7 +127,7 @@ export function assertPlatform(requiredToken: any): PlatformRef { * @experimental APIs related to application bootstrap are currently under review. */ export function destroyPlatform(): void { - if (isPresent(_platform) && !_platform.destroyed) { + if (_platform && !_platform.destroyed) { _platform.destroy(); } } @@ -135,7 +138,7 @@ export function destroyPlatform(): void { * @experimental APIs related to application bootstrap are currently under review. */ export function getPlatform(): PlatformRef { - return isPresent(_platform) && !_platform.destroyed ? _platform : null; + return _platform && !_platform.destroyed ? _platform : null; } /** @@ -224,9 +227,9 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () = // rethrow as the exception handler might not do it throw e; }); - } else { - return result; } + + return result; } catch (e) { errorHandler.handleError(e); // rethrow as the exception handler might not do it @@ -238,7 +241,6 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () = export class PlatformRef_ extends PlatformRef { private _modules: NgModuleRef[] = []; private _destroyListeners: Function[] = []; - private _destroyed: boolean = false; constructor(private _injector: Injector) { super(); } @@ -253,8 +255,8 @@ export class PlatformRef_ extends PlatformRef { if (this._destroyed) { throw new Error('The platform has already been destroyed!'); } - ListWrapper.clone(this._modules).forEach((app) => app.destroy()); - this._destroyListeners.forEach((dispose) => dispose()); + this._modules.slice().forEach(module => module.destroy()); + this._destroyListeners.forEach(listener => listener()); this._destroyed = true; } @@ -301,7 +303,7 @@ export class PlatformRef_ extends PlatformRef { componentFactoryCallback?: any): Promise> { const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory); const compiler = compilerFactory.createCompiler( - compilerOptions instanceof Array ? compilerOptions : [compilerOptions]); + Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]); // ugly internal api hack: generate host component factories for all declared components and // pass the factories into the callback - this is used by UpdateAdapter to get hold of all @@ -424,10 +426,10 @@ export class ApplicationRef_ extends ApplicationRef { componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory); } this._rootComponentTypes.push(componentFactory.componentType); - var compRef = componentFactory.create(this._injector, [], componentFactory.selector); + const compRef = componentFactory.create(this._injector, [], componentFactory.selector); compRef.onDestroy(() => { this._unloadComponent(compRef); }); - var testability = compRef.injector.get(Testability, null); - if (isPresent(testability)) { + const testability = compRef.injector.get(Testability, null); + if (testability) { compRef.injector.get(TestabilityRegistry) .registerApplication(compRef.location.nativeElement, testability); } @@ -454,7 +456,7 @@ export class ApplicationRef_ extends ApplicationRef { /** @internal */ _unloadComponent(componentRef: ComponentRef): void { - if (!ListWrapper.contains(this._rootComponents, componentRef)) { + if (this._rootComponents.indexOf(componentRef) == -1) { return; } this.unregisterChangeDetector(componentRef.changeDetectorRef); @@ -466,7 +468,7 @@ export class ApplicationRef_ extends ApplicationRef { throw new Error('ApplicationRef.tick is called recursively'); } - var s = ApplicationRef_._tickScope(); + const scope = ApplicationRef_._tickScope(); try { this._runningTick = true; this._changeDetectorRefs.forEach((detector) => detector.detectChanges()); @@ -475,13 +477,13 @@ export class ApplicationRef_ extends ApplicationRef { } } finally { this._runningTick = false; - wtfLeave(s); + wtfLeave(scope); } } ngOnDestroy() { // TODO(alxhub): Dispose of the NgZone. - ListWrapper.clone(this._rootComponents).forEach((ref) => ref.destroy()); + this._rootComponents.slice().forEach((component) => component.destroy()); } get componentTypes(): Type[] { return this._rootComponentTypes; } diff --git a/modules/@angular/core/src/application_tokens.ts b/modules/@angular/core/src/application_tokens.ts index 8b8a0f5ac9fc..12a7be59ce3b 100644 --- a/modules/@angular/core/src/application_tokens.ts +++ b/modules/@angular/core/src/application_tokens.ts @@ -34,7 +34,7 @@ export function _appIdRandomProviderFactory() { export const APP_ID_RANDOM_PROVIDER = { provide: APP_ID, useFactory: _appIdRandomProviderFactory, - deps: [] + deps: [], }; function _randomChar(): string { diff --git a/modules/@angular/core/src/linker/view_utils.ts b/modules/@angular/core/src/linker/view_utils.ts index 520039ab061b..c10b575a5ad4 100644 --- a/modules/@angular/core/src/linker/view_utils.ts +++ b/modules/@angular/core/src/linker/view_utils.ts @@ -10,7 +10,6 @@ import {APP_ID} from '../application_tokens'; import {devModeEqual} from '../change_detection/change_detection'; import {UNINITIALIZED} from '../change_detection/change_detection_util'; import {Inject, Injectable} from '../di'; -import {ListWrapper} from '../facade/collection'; import {isBlank, isPresent, looseIdentical} from '../facade/lang'; import {ViewEncapsulation} from '../metadata/view'; import {RenderComponentType, Renderer, RootRenderer} from '../render/api'; diff --git a/modules/@angular/core/src/metadata.ts b/modules/@angular/core/src/metadata.ts index f8c4ca9a2abe..e606c12fa4a3 100644 --- a/modules/@angular/core/src/metadata.ts +++ b/modules/@angular/core/src/metadata.ts @@ -15,8 +15,6 @@ import {Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module'; import {ViewEncapsulation} from './metadata/view'; -import {Type} from './type'; -import {TypeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators'; export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di'; export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; diff --git a/modules/@angular/core/src/metadata/view.ts b/modules/@angular/core/src/metadata/view.ts index f917112eac39..c1e28b8ddd52 100644 --- a/modules/@angular/core/src/metadata/view.ts +++ b/modules/@angular/core/src/metadata/view.ts @@ -7,8 +7,6 @@ */ import {AnimationEntryMetadata} from '../animation/metadata'; -import {Type} from '../type'; - /** * Defines template and style encapsulation options available for Component's {@link Component}. @@ -46,13 +44,6 @@ export var VIEW_ENCAPSULATION_VALUES = /** * Metadata properties available for configuring Views. * - * Each Angular component requires a single `@Component` and at least one `@View` annotation. The - * `@View` annotation specifies the HTML template to use, and lists the directives that are active - * within the template. - * - * When a component is instantiated, the template is loaded into the component's shadow root, and - * the expressions and statements in the template are evaluated against the component. - * * For details on the `@Component` annotation, see {@link Component}. * * ### Example @@ -61,7 +52,6 @@ export var VIEW_ENCAPSULATION_VALUES = * @Component({ * selector: 'greet', * template: 'Hello {{name}}!', - * directives: [GreetUser, Bold] * }) * class Greet { * name: string; @@ -73,46 +63,23 @@ export var VIEW_ENCAPSULATION_VALUES = * ``` * * @deprecated Use Component instead. + * + * {@link Component} */ export class ViewMetadata { - /** - * Specifies a template URL for an Angular component. - * - * NOTE: Only one of `templateUrl` or `template` can be defined per View. - * - * - */ + /** {@link Component.templateUrl} */ templateUrl: string; - - /** - * Specifies an inline template for an Angular component. - * - * NOTE: Only one of `templateUrl` or `template` can be defined per View. - */ + /** {@link Component.template} */ template: string; - - /** - * Specifies stylesheet URLs for an Angular component. - * - * - */ + /** {@link Component.stylesUrl} */ styleUrls: string[]; - - /** - * Specifies an inline stylesheet for an Angular component. - */ + /** {@link Component.styles} */ styles: string[]; - - /** - * Specify how the template and the styles should be encapsulated. - * The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view - * has styles, - * otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}. - */ + /** {@link Component.encapsulation} */ encapsulation: ViewEncapsulation; - + /** {@link Component.animation} */ animations: AnimationEntryMetadata[]; - + /** {@link Component.interpolation} */ interpolation: [string, string]; constructor( diff --git a/modules/@angular/core/src/platform_core_providers.ts b/modules/@angular/core/src/platform_core_providers.ts index 5b977201c775..e5a71a01543d 100644 --- a/modules/@angular/core/src/platform_core_providers.ts +++ b/modules/@angular/core/src/platform_core_providers.ts @@ -8,7 +8,7 @@ import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref'; import {Console} from './console'; -import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './di'; +import {Provider} from './di'; import {Reflector, reflector} from './reflection/reflection'; import {ReflectorReader} from './reflection/reflector_reader'; import {TestabilityRegistry} from './testability/testability'; @@ -18,9 +18,12 @@ function _reflector(): Reflector { } const _CORE_PLATFORM_PROVIDERS: Provider[] = [ - PlatformRef_, {provide: PlatformRef, useExisting: PlatformRef_}, + PlatformRef_, + {provide: PlatformRef, useExisting: PlatformRef_}, {provide: Reflector, useFactory: _reflector, deps: []}, - {provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, Console + {provide: ReflectorReader, useExisting: Reflector}, + TestabilityRegistry, + Console, ]; /** diff --git a/modules/@angular/core/src/type.ts b/modules/@angular/core/src/type.ts index 2ea9323a050b..21e6e29fad52 100644 --- a/modules/@angular/core/src/type.ts +++ b/modules/@angular/core/src/type.ts @@ -16,7 +16,7 @@ * * @stable */ -export var Type = Function; +export const Type = Function; export interface Type extends Function { new (...args: any[]): T; } diff --git a/tools/public_api_guard/core/index.d.ts b/tools/public_api_guard/core/index.d.ts index d798eb2daf05..49f6ed19fcad 100644 --- a/tools/public_api_guard/core/index.d.ts +++ b/tools/public_api_guard/core/index.d.ts @@ -916,7 +916,7 @@ export declare const TRANSLATIONS_FORMAT: OpaqueToken; export declare function trigger(name: string, animation: AnimationMetadata[]): AnimationEntryMetadata; /** @stable */ -export declare var Type: FunctionConstructor; +export declare const Type: FunctionConstructor; /** @stable */ export interface TypeDecorator { From 98fac36706530c2251bd0c63aa9ac0a3deaf46bb Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 20 Sep 2016 14:21:06 -0700 Subject: [PATCH 13/70] docs(Component): API docs for .encapsulation and .interpolation --- .../@angular/core/src/metadata/directives.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/modules/@angular/core/src/metadata/directives.ts b/modules/@angular/core/src/metadata/directives.ts index 38baf9295842..7a1cf43becd8 100644 --- a/modules/@angular/core/src/metadata/directives.ts +++ b/modules/@angular/core/src/metadata/directives.ts @@ -448,15 +448,15 @@ export interface ComponentDecorator { * * **changeDetection** - change detection strategy used by this component * * **encapsulation** - style encapsulation strategy used by this component * * **entryComponents** - list of components that are dynamically inserted into the view of this - * component + * component * * **exportAs** - name under which the component instance is exported in a template * * **host** - map of class property to host element bindings for events, properties and - * attributes + * attributes * * **inputs** - list of class property names to data-bind as component inputs * * **interpolation** - custom interpolation markers used in this component's template * * **moduleId** - ES/CommonJS module id of the file in which this component is defined * * **outputs** - list of class property names that expose output events that others can - * subscribe to + * subscribe to * * **providers** - list of providers available to this component and its children * * **queries** - configure queries that can be injected into the component * * **selector** - css selector that identifies this component in a template @@ -655,20 +655,33 @@ export interface Component extends Directive { animations?: AnimationEntryMetadata[]; /** - * Specify how the template and the styles should be encapsulated. - * The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view - * has styles, - * otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}. + * Specifies how the template and the styles should be encapsulated: + * - {@link ViewEncapsulation#Native `ViewEncapsulation.Native`} to use shadow roots - only works + * if natively available on the platform, + * - {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} to use shimmed CSS that + * emulates the native behavior, + * - {@link ViewEncapsulation#None `ViewEncapsulation.None`} to use global CSS without any + * encapsulation. + * + * When no `encapsulation` is defined for the component, the default value from the + * {@link CompilerConfig} is used. The default is `ViewEncapsulation.Emulated`}. Provide a new + * `CompilerConfig` to override this value. + * + * If the encapsulation is set to `ViewEncapsulation.Emulated` and the component has no `styles` + * nor `styleUrls` the encapsulation will automatically be switched to `ViewEncapsulation.None`. */ encapsulation?: ViewEncapsulation; + /** + * Overrides the default encapsulation start and end delimiters (respectively `{{` and `}}`) + */ interpolation?: [string, string]; /** * Defines the components that should be compiled as well when * this component is defined. For each components listed here, - * Angular will create a {@link ComponentFactory ComponentFactory} and store it in the - * {@link ComponentFactoryResolver ComponentFactoryResolver}. + * Angular will create a {@link ComponentFactory} and store it in the + * {@link ComponentFactoryResolver}. */ entryComponents?: Array|any[]>; } From b81e2e7a31829de3ea0dc56dcb1243425700464e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Fri, 23 Sep 2016 10:47:16 -0700 Subject: [PATCH 14/70] fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808) fixes #11280 --- .../@angular/upgrade/src/upgrade_adapter.ts | 9 ++++--- modules/@angular/upgrade/test/upgrade_spec.ts | 27 ++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/modules/@angular/upgrade/src/upgrade_adapter.ts b/modules/@angular/upgrade/src/upgrade_adapter.ts index 14566492b254..0126d0f068b1 100644 --- a/modules/@angular/upgrade/src/upgrade_adapter.ts +++ b/modules/@angular/upgrade/src/upgrade_adapter.ts @@ -401,9 +401,12 @@ export class UpgradeAdapter { ._bootstrapModuleWithZone( DynamicNgUpgradeModule, undefined, ngZone, (componentFactories: ComponentFactory[]) => { - componentFactories.forEach((componentFactory) => { - componentFactoryRefMap[getComponentInfo(componentFactory.componentType) - .selector] = componentFactory; + componentFactories.forEach((componentFactory: ComponentFactory) => { + var type: Type = componentFactory.componentType; + if (this.upgradedComponents.indexOf(type) !== -1) { + componentFactoryRefMap[getComponentInfo(type).selector] = + componentFactory; + } }); }) .then((ref: NgModuleRef) => { diff --git a/modules/@angular/upgrade/test/upgrade_spec.ts b/modules/@angular/upgrade/test/upgrade_spec.ts index 91cdf7706f10..8aaf51a6fd04 100644 --- a/modules/@angular/upgrade/test/upgrade_spec.ts +++ b/modules/@angular/upgrade/test/upgrade_spec.ts @@ -964,6 +964,31 @@ export function main() { })); }); + it('should allow attribute selectors for components in ng2', async(() => { + const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => MyNg2Module)); + var ng1Module = angular.module('myExample', []); + + @Component({selector: '[works]', template: 'works!'}) + class WorksComponent { + } + + @Component({selector: 'root-component', template: 'It
'}) + class RootComponent { + } + + @NgModule({imports: [BrowserModule], declarations: [RootComponent, WorksComponent]}) + class MyNg2Module { + } + + ng1Module.directive('rootComponent', adapter.downgradeNg2Component(RootComponent)); + + document.body.innerHTML = ''; + adapter.bootstrap(document.body.firstElementChild, ['myExample']).ready((ref) => { + expect(multiTrim(document.body.textContent)).toEqual('It works!'); + ref.dispose(); + }); + })); + describe('examples', () => { it('should verify UpgradeAdapter example', async(() => { const adapter: UpgradeAdapter = new UpgradeAdapter(forwardRef(() => Ng2Module)); @@ -994,7 +1019,7 @@ export function main() { document.body.innerHTML = 'project'; - adapter.bootstrap(document.body, ['myExample']).ready((ref) => { + adapter.bootstrap(document.body.firstElementChild, ['myExample']).ready((ref) => { expect(multiTrim(document.body.textContent)) .toEqual('ng2[ng1[Hello World!](transclude)](project)'); ref.dispose(); From 16601f935985362056a237fcbd6890da078e2d76 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Fri, 23 Sep 2016 10:50:42 -0700 Subject: [PATCH 15/70] docs(changelog): add changelog for 2.0.1 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f864ee961570..32b42b46a148 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ + +## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23) + + +### Bug Fixes + +* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759)) +* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644) +* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652)) +* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590) +* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643) +* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#1645](https://github.com/angular/angular/issues/1645) +* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418)) +* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719) +* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e)) +* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280) + + + # [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14) From 712d1a7c37b5dcd2878f814e64cfe44744f30f28 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Fri, 23 Sep 2016 11:28:36 -0700 Subject: [PATCH 16/70] chore(release): v2.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2a2caab06a9a..188dc609a8b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-srcs", - "version": "2.0.0", + "version": "2.0.1", "private": true, "branchPattern": "2.0.*", "description": "Angular 2 - a web framework for modern web apps", From cf750e17edc89e0d93641ab53a1f6cdaeffa50ec Mon Sep 17 00:00:00 2001 From: Victor Savkin Date: Fri, 23 Sep 2016 15:14:06 -0700 Subject: [PATCH 17/70] fix(router): do not reset the router state when updating the component (#11867) --- modules/@angular/router/src/router.ts | 11 +++++++++++ modules/@angular/router/test/router.spec.ts | 20 +++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index faeec8b13673..f5da74f61315 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -317,6 +317,17 @@ export class Router { this.currentRouterState = createEmptyState(this.currentUrlTree, this.rootComponentType); } + /** + * @internal + * TODO: this should be removed once the constructor of the router made internal + */ + resetRootComponentType(rootComponentType: Type): void { + this.rootComponentType = rootComponentType; + // TODO: vsavkin router 4.0 should make the root component set to null + // this will simplify the lifecycle of the router. + this.currentRouterState.root.component = this.rootComponentType; + } + /** * Sets up the location change listener and performs the initial navigation. */ diff --git a/modules/@angular/router/test/router.spec.ts b/modules/@angular/router/test/router.spec.ts index 42ea05b27b3a..007e6c9b7a2c 100644 --- a/modules/@angular/router/test/router.spec.ts +++ b/modules/@angular/router/test/router.spec.ts @@ -6,13 +6,31 @@ * found in the LICENSE file at https://angular.io/license */ -import {PreActivation} from '../src/router'; +import {TestBed} from '@angular/core/testing'; + +import {PreActivation, Router} from '../src/router'; import {RouterOutletMap} from '../src/router_outlet_map'; import {ActivatedRouteSnapshot, InheritedResolve, RouterStateSnapshot, createEmptyStateSnapshot} from '../src/router_state'; import {DefaultUrlSerializer} from '../src/url_tree'; import {TreeNode} from '../src/utils/tree'; +import {RouterTestingModule} from '../testing/router_testing_module'; describe('Router', () => { + describe('resetRootComponentType', () => { + class NewRootComponent {} + + beforeEach(() => { TestBed.configureTestingModule({imports: [RouterTestingModule]}); }); + + it('should not change root route when updating the root component', () => { + const r: Router = TestBed.get(Router); + const root = r.routerState.root; + + r.resetRootComponentType(NewRootComponent); + + expect(r.routerState.root).toBe(root); + }); + }); + describe('PreActivation', () => { const serializer = new DefaultUrlSerializer(); const inj = {get: (token: any) => () => `${token}_value`}; From 85489a166e6b9b6bf5bee17e411beda9f8f5dad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Fri, 23 Sep 2016 16:37:04 -0400 Subject: [PATCH 18/70] refactor(animations): ensure animation input/outputs are managed within the template parser (#11782) Closes #11782 Closes #11601 Related #11707 --- .../src/animation/animation_compiler.ts | 174 ++---------------- .../src/animation/animation_parser.ts | 102 +++++----- modules/@angular/compiler/src/identifiers.ts | 7 +- .../@angular/compiler/src/offline_compiler.ts | 14 +- .../compiler/src/private_import_core.ts | 2 - .../compiler/src/provider_analyzer.ts | 16 +- .../@angular/compiler/src/runtime_compiler.ts | 14 +- .../src/template_parser/template_ast.ts | 13 +- .../src/template_parser/template_parser.ts | 119 +++++++++--- modules/@angular/compiler/src/util.ts | 14 +- .../src/view_compiler/compile_view.ts | 4 +- .../src/view_compiler/event_binder.ts | 39 ++-- .../src/view_compiler/property_binder.ts | 2 - .../compiler/src/view_compiler/view_binder.ts | 31 +--- .../src/view_compiler/view_builder.ts | 10 +- .../src/view_compiler/view_compiler.ts | 22 +-- .../test/animation/animation_compiler_spec.ts | 21 +-- .../test/animation/animation_parser_spec.ts | 10 +- .../template_parser/template_parser_spec.ts | 44 +++-- .../core/src/animation/animation_output.ts | 10 - .../@angular/core/src/core_private_export.ts | 3 - modules/@angular/core/src/linker/view.ts | 16 +- .../animation/animation_integration_spec.ts | 35 +++- 23 files changed, 324 insertions(+), 398 deletions(-) delete mode 100644 modules/@angular/core/src/animation/animation_output.ts diff --git a/modules/@angular/compiler/src/animation/animation_compiler.ts b/modules/@angular/compiler/src/animation/animation_compiler.ts index f80ac3cc3e41..3399e8e80081 100644 --- a/modules/@angular/compiler/src/animation/animation_compiler.ts +++ b/modules/@angular/compiler/src/animation/animation_compiler.ts @@ -6,75 +6,26 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata} from '../compile_metadata'; import {StringMapWrapper} from '../facade/collection'; import {isBlank, isPresent} from '../facade/lang'; import {Identifiers, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; -import {ANY_STATE, AnimationOutput, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; -import * as t from '../template_parser/template_ast'; +import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core'; -import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast'; -import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry, parseAnimationOutputName} from './animation_parser'; +import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast'; -const animationCompilationCache = - new Map(); - -export class CompiledAnimationTriggerResult { - constructor( - public name: string, public statesMapStatement: o.Statement, - public statesVariableName: string, public fnStatement: o.Statement, - public fnVariable: o.Expression) {} -} - -export class CompiledComponentAnimationResult { - constructor( - public outputs: AnimationOutput[], public triggers: CompiledAnimationTriggerResult[]) {} +export class AnimationEntryCompileResult { + constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {} } export class AnimationCompiler { - compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]): - CompiledComponentAnimationResult { - var compiledAnimations: CompiledAnimationTriggerResult[] = []; - var groupedErrors: string[] = []; - var triggerLookup: {[key: string]: CompiledAnimationTriggerResult} = {}; - var componentName = component.type.name; - - component.template.animations.forEach(entry => { - var result = parseAnimationEntry(entry); - var triggerName = entry.name; - if (result.errors.length > 0) { - var errorMessage = - `Unable to parse the animation sequence for "${triggerName}" due to the following errors:`; - result.errors.forEach( - (error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; }); - groupedErrors.push(errorMessage); - } - - if (triggerLookup[triggerName]) { - groupedErrors.push( - `The animation trigger "${triggerName}" has already been registered on "${componentName}"`); - } else { - var factoryName = `${componentName}_${entry.name}`; - var visitor = new _AnimationBuilder(triggerName, factoryName); - var compileResult = visitor.build(result.ast); - compiledAnimations.push(compileResult); - triggerLookup[entry.name] = compileResult; - } + compile(factoryNamePrefix: string, parsedAnimations: AnimationEntryAst[]): + AnimationEntryCompileResult[] { + return parsedAnimations.map(entry => { + const factoryName = `${factoryNamePrefix}_${entry.name}`; + const visitor = new _AnimationBuilder(entry.name, factoryName); + return visitor.build(entry); }); - - var validatedProperties = _validateAnimationProperties(compiledAnimations, template); - validatedProperties.errors.forEach(error => { groupedErrors.push(error.msg); }); - - if (groupedErrors.length > 0) { - var errorMessageStr = - `Animation parsing for ${component.type.name} has failed due to the following errors:`; - groupedErrors.forEach(error => errorMessageStr += `\n- ${error}`); - throw new Error(errorMessageStr); - } - - animationCompilationCache.set(component, compiledAnimations); - return new CompiledComponentAnimationResult(validatedProperties.outputs, compiledAnimations); } } @@ -334,7 +285,7 @@ class _AnimationBuilder implements AnimationAstVisitor { statements); } - build(ast: AnimationAst): CompiledAnimationTriggerResult { + build(ast: AnimationAst): AnimationEntryCompileResult { var context = new _AnimationBuilderContext(); var fnStatement = ast.visit(this, context).toDeclStmt(this._fnVarName); var fnVariable = o.variable(this._fnVarName); @@ -353,9 +304,10 @@ class _AnimationBuilder implements AnimationAstVisitor { lookupMap.push([stateName, variableValue]); }); - var compiledStatesMapExpr = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt(); - return new CompiledAnimationTriggerResult( - this.animationName, compiledStatesMapExpr, this._statesMapVarName, fnStatement, fnVariable); + const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt(); + const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement]; + + return new AnimationEntryCompileResult(this.animationName, statements, fnVariable); } } @@ -405,99 +357,3 @@ function _isEndStateAnimateStep(step: AnimationAst): boolean { function _getStylesArray(obj: any): {[key: string]: any}[] { return obj.styles.styles; } - -function _validateAnimationProperties( - compiledAnimations: CompiledAnimationTriggerResult[], - template: t.TemplateAst[]): AnimationPropertyValidationOutput { - var visitor = new _AnimationTemplatePropertyVisitor(compiledAnimations); - t.templateVisitAll(visitor, template); - return new AnimationPropertyValidationOutput(visitor.outputs, visitor.errors); -} - -export class AnimationPropertyValidationOutput { - constructor(public outputs: AnimationOutput[], public errors: AnimationParseError[]) {} -} - -class _AnimationTemplatePropertyVisitor implements t.TemplateAstVisitor { - private _animationRegistry: {[key: string]: boolean}; - public errors: AnimationParseError[] = []; - public outputs: AnimationOutput[] = []; - - constructor(animations: CompiledAnimationTriggerResult[]) { - this._animationRegistry = this._buildCompileAnimationLookup(animations); - } - - private _buildCompileAnimationLookup(animations: CompiledAnimationTriggerResult[]): - {[key: string]: boolean} { - var map: {[key: string]: boolean} = {}; - animations.forEach(entry => { map[entry.name] = true; }); - return map; - } - - private _validateAnimationInputOutputPairs( - inputAsts: t.BoundElementPropertyAst[], outputAsts: t.BoundEventAst[], - animationRegistry: {[key: string]: any}, isHostLevel: boolean): void { - var detectedAnimationInputs: {[key: string]: boolean} = {}; - inputAsts.forEach(input => { - if (input.type == t.PropertyBindingType.Animation) { - var triggerName = input.name; - if (isPresent(animationRegistry[triggerName])) { - detectedAnimationInputs[triggerName] = true; - } else { - this.errors.push( - new AnimationParseError(`Couldn't find an animation entry for ${triggerName}`)); - } - } - }); - - outputAsts.forEach(output => { - if (output.name[0] == '@') { - var normalizedOutputData = parseAnimationOutputName(output.name.substr(1), this.errors); - let triggerName = normalizedOutputData.name; - let triggerEventPhase = normalizedOutputData.phase; - if (!animationRegistry[triggerName]) { - this.errors.push(new AnimationParseError( - `Couldn't find the corresponding ${isHostLevel ? 'host-level ' : '' }animation trigger definition for (@${triggerName})`)); - } else if (!detectedAnimationInputs[triggerName]) { - this.errors.push(new AnimationParseError( - `Unable to listen on (@${triggerName}.${triggerEventPhase}) because the animation trigger [@${triggerName}] isn't being used on the same element`)); - } else { - this.outputs.push(normalizedOutputData); - } - } - }); - } - - visitElement(ast: t.ElementAst, ctx: any): any { - this._validateAnimationInputOutputPairs( - ast.inputs, ast.outputs, this._animationRegistry, false); - - var componentOnElement: t.DirectiveAst = - ast.directives.find(directive => directive.directive.isComponent); - if (componentOnElement) { - let cachedComponentAnimations = animationCompilationCache.get(componentOnElement.directive); - if (cachedComponentAnimations) { - this._validateAnimationInputOutputPairs( - componentOnElement.hostProperties, componentOnElement.hostEvents, - this._buildCompileAnimationLookup(cachedComponentAnimations), true); - } - } - - t.templateVisitAll(this, ast.children); - } - - visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst, ctx: any): any { - t.templateVisitAll(this, ast.children); - } - - visitEvent(ast: t.BoundEventAst, ctx: any): any {} - visitBoundText(ast: t.BoundTextAst, ctx: any): any {} - visitText(ast: t.TextAst, ctx: any): any {} - visitNgContent(ast: t.NgContentAst, ctx: any): any {} - visitAttr(ast: t.AttrAst, ctx: any): any {} - visitDirective(ast: t.DirectiveAst, ctx: any): any {} - visitReference(ast: t.ReferenceAst, ctx: any): any {} - visitVariable(ast: t.VariableAst, ctx: any): any {} - visitDirectiveProperty(ast: t.BoundDirectivePropertyAst, ctx: any): any {} - visitElementProperty(ast: t.BoundElementPropertyAst, ctx: any): any {} -} diff --git a/modules/@angular/compiler/src/animation/animation_parser.ts b/modules/@angular/compiler/src/animation/animation_parser.ts index 4870b2804d9d..bf5b7177e3b6 100644 --- a/modules/@angular/compiler/src/animation/animation_parser.ts +++ b/modules/@angular/compiler/src/animation/animation_parser.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata} from '../compile_metadata'; +import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata'; import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {isArray, isBlank, isPresent, isString, isStringMap} from '../facade/lang'; import {Math} from '../facade/math'; import {ParseError} from '../parse_util'; +import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core'; -import {ANY_STATE, AnimationOutput, FILL_STYLE_FLAG} from '../private_import_core'; import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast'; import {StylesCollection} from './styles_collection'; @@ -21,62 +21,70 @@ const _TERMINAL_KEYFRAME = 1; const _ONE_SECOND = 1000; export class AnimationParseError extends ParseError { - constructor(message: any /** TODO #9100 */) { super(null, message); } + constructor(message: string) { super(null, message); } toString(): string { return `${this.msg}`; } } -export class ParsedAnimationResult { +export class AnimationEntryParseResult { constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {} } -export function parseAnimationEntry(entry: CompileAnimationEntryMetadata): ParsedAnimationResult { - var errors: AnimationParseError[] = []; - var stateStyles: {[key: string]: AnimationStylesAst} = {}; - var transitions: CompileAnimationStateTransitionMetadata[] = []; - - var stateDeclarationAsts: any[] /** TODO #9100 */ = []; - entry.definitions.forEach(def => { - if (def instanceof CompileAnimationStateDeclarationMetadata) { - _parseAnimationDeclarationStates(def, errors).forEach(ast => { - stateDeclarationAsts.push(ast); - stateStyles[ast.stateName] = ast.styles; - }); - } else { - transitions.push(def); +export class AnimationParser { + parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] { + const errors: string[] = []; + const componentName = component.type.name; + const animationTriggerNames = new Set(); + const asts = component.template.animations.map(entry => { + const result = this.parseEntry(entry); + const ast = result.ast; + const triggerName = ast.name; + if (animationTriggerNames.has(triggerName)) { + result.errors.push(new AnimationParseError( + `The animation trigger "${triggerName}" has already been registered for the ${componentName} component`)); + } else { + animationTriggerNames.add(triggerName); + } + if (result.errors.length > 0) { + let errorMessage = + `- Unable to parse the animation sequence for "${triggerName}" on the ${componentName} component due to the following errors:`; + result.errors.forEach( + (error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; }); + errors.push(errorMessage); + } + return ast; + }); + + if (errors.length > 0) { + const errorString = errors.join('\n'); + throw new Error(`Animation parse errors:\n${errorString}`); } - }); - var stateTransitionAsts = - transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors)); + return asts; + } - var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts); - return new ParsedAnimationResult(ast, errors); -} + parseEntry(entry: CompileAnimationEntryMetadata): AnimationEntryParseResult { + var errors: AnimationParseError[] = []; + var stateStyles: {[key: string]: AnimationStylesAst} = {}; + var transitions: CompileAnimationStateTransitionMetadata[] = []; + + var stateDeclarationAsts: AnimationStateDeclarationAst[] = []; + entry.definitions.forEach(def => { + if (def instanceof CompileAnimationStateDeclarationMetadata) { + _parseAnimationDeclarationStates(def, errors).forEach(ast => { + stateDeclarationAsts.push(ast); + stateStyles[ast.stateName] = ast.styles; + }); + } else { + transitions.push(def); + } + }); -export function parseAnimationOutputName( - outputName: string, errors: AnimationParseError[]): AnimationOutput { - var values = outputName.split('.'); - var name: string; - var phase: string = ''; - if (values.length > 1) { - name = values[0]; - let parsedPhase = values[1]; - switch (parsedPhase) { - case 'start': - case 'done': - phase = parsedPhase; - break; - - default: - errors.push(new AnimationParseError( - `The provided animation output phase value "${parsedPhase}" for "@${name}" is not supported (use start or done)`)); - } - } else { - name = outputName; - errors.push(new AnimationParseError( - `The animation trigger output event (@${name}) is missing its phase value name (start or done are currently supported)`)); + var stateTransitionAsts = + transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors)); + + var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts); + return new AnimationEntryParseResult(ast, errors); } - return new AnimationOutput(name, phase, outputName); } function _parseAnimationDeclarationStates( diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index a81eeb81c9e2..f1008b45c776 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -9,7 +9,7 @@ import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata'; -import {AnimationGroupPlayer, AnimationKeyframe, AnimationOutput, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core'; +import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core'; import {assetUrl} from './util'; var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view'); @@ -266,11 +266,6 @@ export class Identifiers { moduleUrl: assetUrl('core', 'i18n/tokens'), runtime: TRANSLATIONS_FORMAT_ }; - static AnimationOutput: IdentifierSpec = { - name: 'AnimationOutput', - moduleUrl: assetUrl('core', 'animation/animation_output'), - runtime: AnimationOutput - }; } export function resolveIdentifier(identifier: IdentifierSpec) { diff --git a/modules/@angular/compiler/src/offline_compiler.ts b/modules/@angular/compiler/src/offline_compiler.ts index 90bd832096c4..65a9bb285687 100644 --- a/modules/@angular/compiler/src/offline_compiler.ts +++ b/modules/@angular/compiler/src/offline_compiler.ts @@ -8,6 +8,8 @@ import {SchemaMetadata} from '@angular/core'; +import {AnimationCompiler} from './animation/animation_compiler'; +import {AnimationParser} from './animation/animation_parser'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata'; import {DirectiveNormalizer} from './directive_normalizer'; import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers'; @@ -28,6 +30,9 @@ export class NgModulesSummary { } export class OfflineCompiler { + private _animationParser = new AnimationParser(); + private _animationCompiler = new AnimationCompiler(); + constructor( private _metadataResolver: CompileMetadataResolver, private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser, @@ -162,14 +167,19 @@ export class OfflineCompiler { compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet, fileSuffix: string, targetStatements: o.Statement[]): string { + const parsedAnimations = this._animationParser.parseComponent(compMeta); const parsedTemplate = this._templateParser.parse( compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name); const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]); - const viewResult = - this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes); + const compiledAnimations = + this._animationCompiler.compile(compMeta.type.name, parsedAnimations); + const viewResult = this._viewCompiler.compileComponent( + compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations); if (componentStyles) { targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix)); } + compiledAnimations.forEach( + entry => { entry.statements.forEach(statement => { targetStatements.push(statement); }); }); targetStatements.push(..._resolveViewStatements(viewResult)); return viewResult.viewFactoryVar; } diff --git a/modules/@angular/compiler/src/private_import_core.ts b/modules/@angular/compiler/src/private_import_core.ts index 5bd4cd4df19e..86e66c6730d8 100644 --- a/modules/@angular/compiler/src/private_import_core.ts +++ b/modules/@angular/compiler/src/private_import_core.ts @@ -75,8 +75,6 @@ export type AnimationKeyframe = typeof r._AnimationKeyframe; export const AnimationKeyframe: typeof r.AnimationKeyframe = r.AnimationKeyframe; export type AnimationStyles = typeof r._AnimationStyles; export const AnimationStyles: typeof r.AnimationStyles = r.AnimationStyles; -export type AnimationOutput = typeof r._AnimationOutput; -export const AnimationOutput: typeof r.AnimationOutput = r.AnimationOutput; export const ANY_STATE = r.ANY_STATE; export const DEFAULT_STATE = r.DEFAULT_STATE; export const EMPTY_STATE = r.EMPTY_STATE; diff --git a/modules/@angular/compiler/src/provider_analyzer.ts b/modules/@angular/compiler/src/provider_analyzer.ts index 52f7c7114226..b6160788da70 100644 --- a/modules/@angular/compiler/src/provider_analyzer.ts +++ b/modules/@angular/compiler/src/provider_analyzer.ts @@ -50,14 +50,14 @@ export class ProviderElementContext { private _hasViewContainer: boolean = false; constructor( - private _viewContext: ProviderViewContext, private _parent: ProviderElementContext, + public viewContext: ProviderViewContext, private _parent: ProviderElementContext, private _isViewRoot: boolean, private _directiveAsts: DirectiveAst[], attrs: AttrAst[], refs: ReferenceAst[], private _sourceSpan: ParseSourceSpan) { this._attrs = {}; attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value); var directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive); this._allProviders = - _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, _viewContext.errors); + _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors); this._contentQueries = _getContentQueries(directivesMeta); var queriedTokens = new Map(); MapWrapper.values(this._allProviders).forEach((provider) => { @@ -124,7 +124,7 @@ export class ProviderElementContext { } currentEl = currentEl._parent; } - queries = this._viewContext.viewQueries.get(token.reference); + queries = this.viewContext.viewQueries.get(token.reference); if (isPresent(queries)) { ListWrapper.addAll(result, queries); } @@ -150,7 +150,7 @@ export class ProviderElementContext { return transformedProviderAst; } if (isPresent(this._seenProviders.get(token.reference))) { - this._viewContext.errors.push(new ProviderError( + this.viewContext.errors.push(new ProviderError( `Cannot instantiate cyclic dependency! ${token.name}`, this._sourceSpan)); return null; } @@ -254,9 +254,9 @@ export class ProviderElementContext { } // check @Host restriction if (isBlank(result)) { - if (!dep.isHost || this._viewContext.component.type.isHost || - this._viewContext.component.type.reference === dep.token.reference || - isPresent(this._viewContext.viewProviders.get(dep.token.reference))) { + if (!dep.isHost || this.viewContext.component.type.isHost || + this.viewContext.component.type.reference === dep.token.reference || + isPresent(this.viewContext.viewProviders.get(dep.token.reference))) { result = dep; } else { result = dep.isOptional ? @@ -266,7 +266,7 @@ export class ProviderElementContext { } } if (isBlank(result)) { - this._viewContext.errors.push( + this.viewContext.errors.push( new ProviderError(`No provider for ${dep.token.name}`, this._sourceSpan)); } return result; diff --git a/modules/@angular/compiler/src/runtime_compiler.ts b/modules/@angular/compiler/src/runtime_compiler.ts index 768380339ecc..1ca88871c337 100644 --- a/modules/@angular/compiler/src/runtime_compiler.ts +++ b/modules/@angular/compiler/src/runtime_compiler.ts @@ -7,7 +7,8 @@ */ import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, Optional, Provider, SchemaMetadata, SkipSelf, Type} from '@angular/core'; - +import {AnimationCompiler} from './animation/animation_compiler'; +import {AnimationParser} from './animation/animation_parser'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata'; import {CompilerConfig} from './config'; import {DirectiveNormalizer} from './directive_normalizer'; @@ -23,8 +24,6 @@ import {TemplateParser} from './template_parser/template_parser'; import {SyncAsyncResult} from './util'; import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler'; - - /** * An internal module of the Angular compiler that begins with component types, * extracts templates, and eventually produces a compiled version of the component @@ -39,6 +38,8 @@ export class RuntimeCompiler implements Compiler { private _compiledTemplateCache = new Map, CompiledTemplate>(); private _compiledHostTemplateCache = new Map, CompiledTemplate>(); private _compiledNgModuleCache = new Map, NgModuleFactory>(); + private _animationParser = new AnimationParser(); + private _animationCompiler = new AnimationCompiler(); constructor( private _injector: Injector, private _metadataResolver: CompileMetadataResolver, @@ -253,12 +254,15 @@ export class RuntimeCompiler implements Compiler { stylesCompileResult.componentStylesheet, externalStylesheetsByModuleUrl); const viewCompMetas = template.viewComponentTypes.map( (compType) => this._assertComponentLoaded(compType, false).normalizedCompMeta); + const parsedAnimations = this._animationParser.parseComponent(compMeta); const parsedTemplate = this._templateParser.parse( compMeta, compMeta.template.template, template.viewDirectives.concat(viewCompMetas), template.viewPipes, template.schemas, compMeta.type.name); + const compiledAnimations = + this._animationCompiler.compile(compMeta.type.name, parsedAnimations); const compileResult = this._viewCompiler.compileComponent( compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar), - template.viewPipes); + template.viewPipes, compiledAnimations); compileResult.dependencies.forEach((dep) => { let depTemplate: CompiledTemplate; if (dep instanceof ViewFactoryDependency) { @@ -275,6 +279,8 @@ export class RuntimeCompiler implements Compiler { }); const statements = stylesCompileResult.componentStylesheet.statements.concat(compileResult.statements); + compiledAnimations.forEach( + entry => { entry.statements.forEach(statement => { statements.push(statement); }); }); let factory: any; if (!this._compilerConfig.useJit) { factory = interpretStatements(statements, compileResult.viewFactoryVar); diff --git a/modules/@angular/compiler/src/template_parser/template_ast.ts b/modules/@angular/compiler/src/template_parser/template_ast.ts index 11f57a0e43e8..d8d916e1739a 100644 --- a/modules/@angular/compiler/src/template_parser/template_ast.ts +++ b/modules/@angular/compiler/src/template_parser/template_ast.ts @@ -12,11 +12,8 @@ import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} import {AST} from '../expression_parser/ast'; import {isPresent} from '../facade/lang'; import {ParseSourceSpan} from '../parse_util'; - import {LifecycleHooks} from '../private_import_core'; - - /** * An Abstract Syntax Tree node representing part of a parsed Angular template. */ @@ -61,7 +58,8 @@ export class AttrAst implements TemplateAst { } /** - * A binding for an element property (e.g. `[property]="expression"`). + * A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g. + * `[@trigger]="stateExp"`) */ export class BoundElementPropertyAst implements TemplateAst { constructor( @@ -71,14 +69,16 @@ export class BoundElementPropertyAst implements TemplateAst { visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitElementProperty(this, context); } + get isAnimation(): boolean { return this.type === PropertyBindingType.Animation; } } /** - * A binding for an element event (e.g. `(event)="handler()"`). + * A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g. + * `(@trigger.phase)="callback($event)"`). */ export class BoundEventAst implements TemplateAst { constructor( - public name: string, public target: string, public handler: AST, + public name: string, public target: string, public phase: string, public handler: AST, public sourceSpan: ParseSourceSpan) {} visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitEvent(this, context); @@ -90,6 +90,7 @@ export class BoundEventAst implements TemplateAst { return this.name; } } + get isAnimation(): boolean { return !!this.phase; } } /** diff --git a/modules/@angular/compiler/src/template_parser/template_parser.ts b/modules/@angular/compiler/src/template_parser/template_parser.ts index 776ba51ae684..62adb99bcd0d 100644 --- a/modules/@angular/compiler/src/template_parser/template_parser.ts +++ b/modules/@angular/compiler/src/template_parser/template_parser.ts @@ -8,7 +8,7 @@ import {Inject, Injectable, OpaqueToken, Optional, SchemaMetadata, SecurityContext} from '@angular/core'; -import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompilePipeMetadata, CompileTemplateMetadata, CompileTokenMetadata, removeIdentifierDuplicates} from '../compile_metadata'; import {AST, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast'; import {Parser} from '../expression_parser/parser'; import {StringMapWrapper} from '../facade/collection'; @@ -26,12 +26,13 @@ import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer' import {ElementSchemaRegistry} from '../schema/element_schema_registry'; import {CssSelector, SelectorMatcher} from '../selector'; import {isStyleUrlResolvable} from '../style_url_resolver'; -import {splitAtColon} from '../util'; +import {splitAtColon, splitAtPeriod} from '../util'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast'; import {PreparsedElementType, preparseElement} from './template_preparser'; + // Group 1 = "bind-" // Group 2 = "let-" // Group 3 = "ref-/#" @@ -142,7 +143,6 @@ export class TemplateParser { const parseVisitor = new TemplateParseVisitor( providerViewContext, uniqDirectives, uniqPipes, schemas, this._exprParser, this._schemaRegistry); - result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT); errors.push(...parseVisitor.errors, ...providerViewContext.errors); } else { @@ -444,6 +444,15 @@ class TemplateParseVisitor implements html.Visitor { providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); + + this._findComponentDirectives(directiveAsts) + .forEach( + componentDirectiveAst => this._validateElementAnimationInputOutputs( + componentDirectiveAst.hostProperties, componentDirectiveAst.hostEvents, + componentDirectiveAst.directive.template)); + + const componentTemplate = providerContext.viewContext.component.template; + this._validateElementAnimationInputOutputs(elementProps, events, componentTemplate); } if (hasInlineTemplates) { @@ -469,9 +478,36 @@ class TemplateParseVisitor implements html.Visitor { templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex, element.sourceSpan); } + return parsedElement; } + private _validateElementAnimationInputOutputs( + inputs: BoundElementPropertyAst[], outputs: BoundEventAst[], + template: CompileTemplateMetadata) { + const triggerLookup = new Set(); + template.animations.forEach(entry => { triggerLookup.add(entry.name); }); + + const animationInputs = inputs.filter(input => input.isAnimation); + animationInputs.forEach(input => { + const name = input.name; + if (!triggerLookup.has(name)) { + this._reportError(`Couldn't find an animation entry for "${name}"`, input.sourceSpan); + } + }); + + outputs.forEach(output => { + if (output.isAnimation) { + const found = animationInputs.find(input => input.name == output.name); + if (!found) { + this._reportError( + `Unable to listen on (@${output.name}.${output.phase}) because the animation trigger [@${output.name}] isn't being used on the same element`, + output.sourceSpan); + } + } + }); + } + private _parseInlineTemplateBinding( attr: html.Attribute, targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[], targetVars: VariableAst[]): boolean { @@ -533,7 +569,7 @@ class TemplateParseVisitor implements html.Visitor { this._parseReference(identifier, value, srcSpan, targetRefs); } else if (bindParts[KW_ON_IDX]) { - this._parseEvent( + this._parseEventOrAnimationEvent( bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } else if (bindParts[KW_BINDON_IDX]) { @@ -544,7 +580,7 @@ class TemplateParseVisitor implements html.Visitor { bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } else if (bindParts[KW_AT_IDX]) { - if (name[0] == '@' && isPresent(value) && value.length > 0) { + if (_isAnimationLabel(name) && isPresent(value) && value.length > 0) { this._reportError( `Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` + ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, @@ -565,7 +601,7 @@ class TemplateParseVisitor implements html.Visitor { targetAnimationProps); } else if (bindParts[IDENT_EVENT_IDX]) { - this._parseEvent( + this._parseEventOrAnimationEvent( bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents); } } else { @@ -608,7 +644,7 @@ class TemplateParseVisitor implements html.Visitor { targetMatchableAttrs: string[][], targetProps: BoundElementOrDirectiveProperty[], targetAnimationProps: BoundElementPropertyAst[]) { const animatePropLength = ANIMATE_PROP_PREFIX.length; - var isAnimationProp = name[0] == '@'; + var isAnimationProp = _isAnimationLabel(name); var animationPrefixLength = 1; if (name.substring(0, animatePropLength) == ANIMATE_PROP_PREFIX) { isAnimationProp = true; @@ -635,6 +671,7 @@ class TemplateParseVisitor implements html.Visitor { if (!isPresent(expression) || expression.length == 0) { expression = 'null'; } + const ast = this._parseBinding(expression, sourceSpan); targetMatchableAttrs.push([name, ast.source]); targetAnimationProps.push(new BoundElementPropertyAst( @@ -662,20 +699,56 @@ class TemplateParseVisitor implements html.Visitor { private _parseAssignmentEvent( name: string, expression: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { - this._parseEvent( + this._parseEventOrAnimationEvent( `${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents); } + private _parseEventOrAnimationEvent( + name: string, expression: string, sourceSpan: ParseSourceSpan, + targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { + if (_isAnimationLabel(name)) { + name = name.substr(1); + this._parseAnimationEvent(name, expression, sourceSpan, targetEvents); + } else { + this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents); + } + } + + private _parseAnimationEvent( + name: string, expression: string, sourceSpan: ParseSourceSpan, + targetEvents: BoundEventAst[]) { + const matches = splitAtPeriod(name, [name, '']); + const eventName = matches[0]; + const phase = matches[1].toLowerCase(); + if (phase) { + switch (phase) { + case 'start': + case 'done': + const ast = this._parseAction(expression, sourceSpan); + targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan)); + break; + + default: + this._reportError( + `The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, + sourceSpan); + break; + } + } else { + this._reportError( + `The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, + sourceSpan); + } + } + private _parseEvent( name: string, expression: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) { // long format: 'target: eventName' - const parts = splitAtColon(name, [null, name]); - const target = parts[0]; - const eventName = parts[1]; + const [target, eventName] = splitAtColon(name, [null, name]); const ast = this._parseAction(expression, sourceSpan); targetMatchableAttrs.push([name, ast.source]); - targetEvents.push(new BoundEventAst(eventName, target, ast, sourceSpan)); + targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan)); // Don't detect directives for event names for now, // so don't add the event name to the matchableAttrs } @@ -779,7 +852,7 @@ class TemplateParseVisitor implements html.Visitor { if (hostListeners) { StringMapWrapper.forEach(hostListeners, (expression: string, propName: string) => { if (isString(expression)) { - this._parseEvent(propName, expression, sourceSpan, [], targetEventAsts); + this._parseEventOrAnimationEvent(propName, expression, sourceSpan, [], targetEventAsts); } else { this._reportError( `Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, @@ -846,7 +919,7 @@ class TemplateParseVisitor implements html.Visitor { if (parts.length === 1) { var partValue = parts[0]; - if (partValue[0] == '@') { + if (_isAnimationLabel(partValue)) { boundPropertyName = partValue.substr(1); bindingType = PropertyBindingType.Animation; securityContext = SecurityContext.NONE; @@ -922,15 +995,13 @@ class TemplateParseVisitor implements html.Visitor { } } + private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] { + return directives.filter(directive => directive.directive.isComponent); + } + private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] { - const componentTypeNames: string[] = []; - directives.forEach(directive => { - const typeName = directive.directive.type.name; - if (directive.directive.isComponent) { - componentTypeNames.push(typeName); - } - }); - return componentTypeNames; + return this._findComponentDirectives(directives) + .map(directive => directive.directive.type.name); } private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) { @@ -1114,3 +1185,7 @@ export class PipeCollector extends RecursiveAstVisitor { return null; } } + +function _isAnimationLabel(name: string): boolean { + return name[0] == '@'; +} diff --git a/modules/@angular/compiler/src/util.ts b/modules/@angular/compiler/src/util.ts index fd8cf945f3d1..f69eb0c63c26 100644 --- a/modules/@angular/compiler/src/util.ts +++ b/modules/@angular/compiler/src/util.ts @@ -21,9 +21,17 @@ export function camelCaseToDashCase(input: string): string { } export function splitAtColon(input: string, defaultValues: string[]): string[] { - const colonIndex = input.indexOf(':'); - if (colonIndex == -1) return defaultValues; - return [input.slice(0, colonIndex).trim(), input.slice(colonIndex + 1).trim()]; + return _splitAt(input, ':', defaultValues); +} + +export function splitAtPeriod(input: string, defaultValues: string[]): string[] { + return _splitAt(input, '.', defaultValues); +} + +function _splitAt(input: string, character: string, defaultValues: string[]): string[] { + const characterIndex = input.indexOf(character); + if (characterIndex == -1) return defaultValues; + return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()]; } export function sanitizeIdentifier(name: string): string { diff --git a/modules/@angular/compiler/src/view_compiler/compile_view.ts b/modules/@angular/compiler/src/view_compiler/compile_view.ts index 9f78ba78b53d..7d429211cc18 100644 --- a/modules/@angular/compiler/src/view_compiler/compile_view.ts +++ b/modules/@angular/compiler/src/view_compiler/compile_view.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompiledAnimationTriggerResult} from '../animation/animation_compiler'; +import {AnimationEntryCompileResult} from '../animation/animation_compiler'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata'; import {CompilerConfig} from '../config'; import {ListWrapper, MapWrapper} from '../facade/collection'; @@ -72,7 +72,7 @@ export class CompileView implements NameResolver { constructor( public component: CompileDirectiveMetadata, public genConfig: CompilerConfig, public pipeMetas: CompilePipeMetadata[], public styles: o.Expression, - public animations: CompiledAnimationTriggerResult[], public viewIndex: number, + public animations: AnimationEntryCompileResult[], public viewIndex: number, public declarationElement: CompileElement, public templateVariableBindings: string[][]) { this.createMethod = new CompileMethod(this); this.animationBindingsMethod = new CompileMethod(this); diff --git a/modules/@angular/compiler/src/view_compiler/event_binder.ts b/modules/@angular/compiler/src/view_compiler/event_binder.ts index 947e6241f447..72a8a3de2b89 100644 --- a/modules/@angular/compiler/src/view_compiler/event_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/event_binder.ts @@ -11,7 +11,6 @@ import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {StringWrapper, isBlank, isPresent} from '../facade/lang'; import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import * as o from '../output/output_ast'; -import {AnimationOutput} from '../private_import_core'; import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast'; import {CompileBinding} from './compile_binding'; @@ -20,10 +19,6 @@ import {CompileMethod} from './compile_method'; import {EventHandlerVars, ViewProperties} from './constants'; import {convertCdStatementToIr} from './expression_converter'; -export class CompileElementAnimationOutput { - constructor(public listener: CompileEventListener, public output: AnimationOutput) {} -} - export class CompileEventListener { private _method: CompileMethod; private _hasComponentHostListener: boolean = false; @@ -32,13 +27,14 @@ export class CompileEventListener { private _actionResultExprs: o.Expression[] = []; static getOrCreate( - compileElement: CompileElement, eventTarget: string, eventName: string, + compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string, targetEventListeners: CompileEventListener[]): CompileEventListener { var listener = targetEventListeners.find( - listener => listener.eventTarget == eventTarget && listener.eventName == eventName); + listener => listener.eventTarget == eventTarget && listener.eventName == eventName && + listener.eventPhase == eventPhase); if (isBlank(listener)) { listener = new CompileEventListener( - compileElement, eventTarget, eventName, targetEventListeners.length); + compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length); targetEventListeners.push(listener); } return listener; @@ -48,7 +44,7 @@ export class CompileEventListener { constructor( public compileElement: CompileElement, public eventTarget: string, public eventName: string, - listenerIndex: number) { + public eventPhase: string, listenerIndex: number) { this._method = new CompileMethod(compileElement.view); this._methodName = `_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`; @@ -119,7 +115,7 @@ export class CompileEventListener { disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private])); } - listenToAnimation(output: AnimationOutput) { + listenToAnimation() { var outputListener = o.THIS_EXPR.callMethod( 'eventHandler', [o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]); @@ -129,11 +125,8 @@ export class CompileEventListener { .callMethod( 'registerAnimationOutput', [ - this.compileElement.renderNode, - o.importExpr(resolveIdentifier(Identifiers.AnimationOutput)).instantiate([ - o.literal(output.name), o.literal(output.phase) - ]), - outputListener + this.compileElement.renderNode, o.literal(this.eventName), + o.literal(this.eventPhase), outputListener ]) .toStmt(); this.compileElement.view.createMethod.addStmt(stmt); @@ -160,7 +153,7 @@ export function collectEventListeners( hostEvents.forEach((hostEvent) => { compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( - compileElement, hostEvent.target, hostEvent.name, eventListeners); + compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, null, null); }); dirs.forEach((directiveAst) => { @@ -169,7 +162,7 @@ export function collectEventListeners( directiveAst.hostEvents.forEach((hostEvent) => { compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent)); var listener = CompileEventListener.getOrCreate( - compileElement, hostEvent.target, hostEvent.name, eventListeners); + compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners); listener.addAction(hostEvent, directiveAst.directive, directiveInstance); }); }); @@ -190,11 +183,13 @@ export function bindDirectiveOutputs( } export function bindRenderOutputs(eventListeners: CompileEventListener[]) { - eventListeners.forEach(listener => listener.listenToRenderer()); -} - -export function bindAnimationOutputs(eventListeners: CompileElementAnimationOutput[]) { - eventListeners.forEach(entry => { entry.listener.listenToAnimation(entry.output); }); + eventListeners.forEach(listener => { + if (listener.eventPhase) { + listener.listenToAnimation(); + } else { + listener.listenToRenderer(); + } + }); } function convertStmtIntoExpression(stmt: o.Statement): o.Expression { diff --git a/modules/@angular/compiler/src/view_compiler/property_binder.ts b/modules/@angular/compiler/src/view_compiler/property_binder.ts index 026efece67f4..19e5caaaa71a 100644 --- a/modules/@angular/compiler/src/view_compiler/property_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/property_binder.ts @@ -31,8 +31,6 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr { return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: ` } -const _animationViewCheckedFlagMap = new Map(); - function bind( view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr, parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[], diff --git a/modules/@angular/compiler/src/view_compiler/view_binder.ts b/modules/@angular/compiler/src/view_compiler/view_binder.ts index 403e6cfe514c..2f98f0f151e8 100644 --- a/modules/@angular/compiler/src/view_compiler/view_binder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_binder.ts @@ -7,18 +7,16 @@ */ import {ListWrapper} from '../facade/collection'; import {identifierToken} from '../identifiers'; -import {AnimationOutput} from '../private_import_core'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; import {CompileElement, CompileNode} from './compile_element'; import {CompileView} from './compile_view'; -import {CompileElementAnimationOutput, CompileEventListener, bindAnimationOutputs, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder'; +import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder'; import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder'; import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder'; -export function bindView( - view: CompileView, parsedTemplate: TemplateAst[], animationOutputs: AnimationOutput[]): void { - var visitor = new ViewBinderVisitor(view, animationOutputs); +export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void { + var visitor = new ViewBinderVisitor(view); templateVisitAll(visitor, parsedTemplate); view.pipes.forEach( (pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); }); @@ -26,12 +24,8 @@ export function bindView( class ViewBinderVisitor implements TemplateAstVisitor { private _nodeIndex: number = 0; - private _animationOutputsMap: {[key: string]: AnimationOutput} = {}; - constructor(public view: CompileView, public animationOutputs: AnimationOutput[]) { - animationOutputs.forEach( - entry => { this._animationOutputsMap[entry.fullPropertyName] = entry; }); - } + constructor(public view: CompileView) {} visitBoundText(ast: BoundTextAst, parent: CompileElement): any { var node = this.view.nodes[this._nodeIndex++]; @@ -48,22 +42,9 @@ class ViewBinderVisitor implements TemplateAstVisitor { visitElement(ast: ElementAst, parent: CompileElement): any { var compileElement = this.view.nodes[this._nodeIndex++]; var eventListeners: CompileEventListener[] = []; - var animationEventListeners: CompileElementAnimationOutput[] = []; collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => { - // TODO: figure out how to abstract this `if` statement elsewhere - if (entry.eventName[0] == '@') { - let animationOutputName = entry.eventName.substr(1); - let output = this._animationOutputsMap[animationOutputName]; - // no need to report an error here since the parser will - // have caught the missing animation trigger definition - if (output) { - animationEventListeners.push(new CompileElementAnimationOutput(entry, output)); - } - } else { - eventListeners.push(entry); - } + eventListeners.push(entry); }); - bindAnimationOutputs(animationEventListeners); bindRenderInputs(ast.inputs, compileElement); bindRenderOutputs(eventListeners); ast.directives.forEach((directiveAst) => { @@ -108,7 +89,7 @@ class ViewBinderVisitor implements TemplateAstVisitor { var providerInstance = compileElement.instances.get(providerAst.token.reference); bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement); }); - bindView(compileElement.embeddedView, ast.children, this.animationOutputs); + bindView(compileElement.embeddedView, ast.children); return null; } diff --git a/modules/@angular/compiler/src/view_compiler/view_builder.ts b/modules/@angular/compiler/src/view_compiler/view_builder.ts index ace3e903cb34..166b160252aa 100644 --- a/modules/@angular/compiler/src/view_compiler/view_builder.ts +++ b/modules/@angular/compiler/src/view_compiler/view_builder.ts @@ -7,8 +7,6 @@ */ import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core'; - -import {AnimationCompiler} from '../animation/animation_compiler'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata, CompileTypeMetadata} from '../compile_metadata'; import {ListWrapper, SetWrapper, StringMapWrapper} from '../facade/collection'; import {StringWrapper, isPresent} from '../facade/lang'; @@ -65,8 +63,6 @@ export function finishView(view: CompileView, targetStatements: o.Statement[]) { class ViewBuilderVisitor implements TemplateAstVisitor { nestedViewCount: number = 0; - private _animationCompiler = new AnimationCompiler(); - constructor( public view: CompileView, public targetDependencies: Array) {} @@ -279,12 +275,10 @@ class ViewBuilderVisitor implements TemplateAstVisitor { ast.hasViewContainer, true, ast.references); this.view.nodes.push(compileElement); - var compiledAnimations = this._animationCompiler.compileComponent(this.view.component, [ast]); - this.nestedViewCount++; var embeddedView = new CompileView( this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR, - compiledAnimations.triggers, this.view.viewIndex + this.nestedViewCount, compileElement, + this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings); this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies); @@ -518,7 +512,7 @@ function createViewFactory( templateUrlInfo = view.component.template.templateUrl; } if (view.viewIndex === 0) { - var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnVariable])); + var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp])); initRenderCompTypeStmts = [new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR), [ renderCompTypeVar .set(ViewConstructorVars.viewUtils.callMethod( diff --git a/modules/@angular/compiler/src/view_compiler/view_compiler.ts b/modules/@angular/compiler/src/view_compiler/view_compiler.ts index 073c92cb4167..2a67f7bdbd1e 100644 --- a/modules/@angular/compiler/src/view_compiler/view_compiler.ts +++ b/modules/@angular/compiler/src/view_compiler/view_compiler.ts @@ -8,7 +8,7 @@ import {Injectable} from '@angular/core'; -import {AnimationCompiler} from '../animation/animation_compiler'; +import {AnimationCompiler, AnimationEntryCompileResult} from '../animation/animation_compiler'; import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata'; import {CompilerConfig} from '../config'; import * as o from '../output/output_ast'; @@ -34,22 +34,18 @@ export class ViewCompiler { compileComponent( component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression, - pipes: CompilePipeMetadata[]): ViewCompileResult { - var dependencies: Array = []; - var compiledAnimations = this._animationCompiler.compileComponent(component, template); - var statements: o.Statement[] = []; - var animationTriggers = compiledAnimations.triggers; - animationTriggers.forEach(entry => { - statements.push(entry.statesMapStatement); - statements.push(entry.fnStatement); - }); - var view = new CompileView( - component, this._genConfig, pipes, styles, animationTriggers, 0, + pipes: CompilePipeMetadata[], + compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult { + const dependencies: Array = []; + const view = new CompileView( + component, this._genConfig, pipes, styles, compiledAnimations, 0, CompileElement.createNull(), []); + + const statements: o.Statement[] = []; buildView(view, template, dependencies); // Need to separate binding from creation to be able to refer to // variables that have been declared after usage. - bindView(view, template, compiledAnimations.outputs); + bindView(view, template); finishView(view, statements); return new ViewCompileResult(statements, view.viewFactory.name, dependencies); diff --git a/modules/@angular/compiler/test/animation/animation_compiler_spec.ts b/modules/@angular/compiler/test/animation/animation_compiler_spec.ts index 094a3da956a3..bdb6b4f22188 100644 --- a/modules/@angular/compiler/test/animation/animation_compiler_spec.ts +++ b/modules/@angular/compiler/test/animation/animation_compiler_spec.ts @@ -10,7 +10,8 @@ import {AnimationMetadata, animate, group, sequence, style, transition, trigger} import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {StringMapWrapper} from '../../../platform-browser-dynamic/src/facade/collection'; -import {AnimationCompiler, CompiledAnimationTriggerResult} from '../../src/animation/animation_compiler'; +import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animation/animation_compiler'; +import {AnimationParser} from '../../src/animation/animation_parser'; import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata'; import {CompileMetadataResolver} from '../../src/metadata_resolver'; @@ -20,12 +21,13 @@ export function main() { beforeEach( inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; })); - var compiler = new AnimationCompiler(); + const parser = new AnimationParser(); + const compiler = new AnimationCompiler(); var compileAnimations = - (component: CompileDirectiveMetadata): CompiledAnimationTriggerResult => { - var result = compiler.compileComponent(component, []); - return result.triggers[0]; + (component: CompileDirectiveMetadata): AnimationEntryCompileResult[] => { + const parsedAnimations = parser.parseComponent(component); + return compiler.compile(component.type.name, parsedAnimations); }; var compileTriggers = (input: any[]) => { @@ -66,14 +68,5 @@ export function main() { expect(capturedErrorMessage) .toMatch(/Animation states via styles must be prefixed with a ":"/); }); - - it('should throw an error when two or more animation triggers contain the same name', () => { - var t1Data: any[] = []; - var t2Data: any[] = []; - - expect(() => { - compileTriggers([['myTrigger', t1Data], ['myTrigger', t2Data]]); - }).toThrowError(/The animation trigger "myTrigger" has already been registered on "myCmp"/); - }); }); } diff --git a/modules/@angular/compiler/test/animation/animation_parser_spec.ts b/modules/@angular/compiler/test/animation/animation_parser_spec.ts index b5688715205a..1e0fd724554f 100644 --- a/modules/@angular/compiler/test/animation/animation_parser_spec.ts +++ b/modules/@angular/compiler/test/animation/animation_parser_spec.ts @@ -11,7 +11,8 @@ import {AsyncTestCompleter, beforeEach, beforeEachProviders, ddescribe, describe import {expect} from '@angular/platform-browser/testing/matchers'; import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast'; -import {parseAnimationEntry} from '../../src/animation/animation_parser'; +import {AnimationParser} from '../../src/animation/animation_parser'; +import {CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata'; import {StringMapWrapper} from '../../src/facade/collection'; import {CompileMetadataResolver} from '../../src/metadata_resolver'; import {FILL_STYLE_FLAG, flattenStyles} from '../private_import_core'; @@ -46,9 +47,10 @@ export function main() { inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; })); var parseAnimation = (data: AnimationMetadata[]) => { - var entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]); - var compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry); - return parseAnimationEntry(compiledAnimationEntry); + const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]); + const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry); + const parser = new AnimationParser(); + return parser.parseEntry(compiledAnimationEntry); }; var getAnimationAstFromEntryAst = diff --git a/modules/@angular/compiler/test/template_parser/template_parser_spec.ts b/modules/@angular/compiler/test/template_parser/template_parser_spec.ts index 00a4842d46c0..dceedebcd4b3 100644 --- a/modules/@angular/compiler/test/template_parser/template_parser_spec.ts +++ b/modules/@angular/compiler/test/template_parser/template_parser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; +import {CompileAnimationEntryMetadata, CompileDiDependencyMetadata, CompileDirectiveMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTemplateMetadata, CompileTokenMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata'; import {DomElementSchemaRegistry} from '@angular/compiler/src/schema/dom_element_schema_registry'; import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAstType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '@angular/compiler/src/template_parser/template_ast'; @@ -16,7 +16,6 @@ import {SchemaMetadata, SecurityContext, Type} from '@angular/core'; import {Console} from '@angular/core/src/console'; import {TestBed} from '@angular/core/testing'; import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; - import {Identifiers, identifierToken, resolveIdentifierToken} from '../../src/identifiers'; import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../src/ml_parser/interpolation_config'; import {MockSchemaRegistry} from '../../testing/index'; @@ -32,9 +31,9 @@ const MOCK_SCHEMA_REGISTRY = [{ export function main() { var ngIf: CompileDirectiveMetadata; - var parse: - (template: string, directives: CompileDirectiveMetadata[], pipes?: CompilePipeMetadata[]) => - TemplateAst[]; + var parse: ( + template: string, directives: CompileDirectiveMetadata[], pipes?: CompilePipeMetadata[], + schemas?: SchemaMetadata[]) => TemplateAst[]; var console: ArrayConsole; function commonBeforeEach() { @@ -43,14 +42,18 @@ export function main() { TestBed.configureCompiler({providers: [{provide: Console, useValue: console}]}); }); beforeEach(inject([TemplateParser], (parser: TemplateParser) => { + var someAnimation = new CompileAnimationEntryMetadata('someAnimation', []); + var someTemplate = new CompileTemplateMetadata({animations: [someAnimation]}); var component = CompileDirectiveMetadata.create({ selector: 'root', + template: someTemplate, type: new CompileTypeMetadata( {moduleUrl: someModuleUrl, name: 'Root', reference: {} as Type}), isComponent: true }); ngIf = CompileDirectiveMetadata.create({ selector: '[ngIf]', + template: someTemplate, type: new CompileTypeMetadata( {moduleUrl: someModuleUrl, name: 'NgIf', reference: {} as Type}), inputs: ['ngIf'] @@ -302,27 +305,31 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. ]); }); - it('should parse bound properties via bind-animate- and not report them as animation properties', + it('should parse bound properties via bind-animate- and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ - [ElementAst, 'div'], - [ - BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null - ] - ]); + expect(humanizeTplAst(parse('
', [], [], []))) + .toEqual([ + [ElementAst, 'div'], + [ + BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', + 'value2', null + ] + ]); }); it('should throw an error when parsing detects non-bound properties via @ that contain a value', () => { - expect(() => { parse('
', []); }) + expect(() => { parse('
', [], [], []); }) .toThrowError( - /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("
\]@something="value2">"\): TestComp@0:5/); + /Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("
\]@someAnimation="value2">"\): TestComp@0:5/); }); it('should not issue a warning when host attributes contain a valid property-bound animation trigger', () => { + const animationEntries = [new CompileAnimationEntryMetadata('prop', [])]; var dirA = CompileDirectiveMetadata.create({ selector: 'div', + template: new CompileTemplateMetadata({animations: animationEntries}), type: new CompileTypeMetadata( {moduleUrl: someModuleUrl, name: 'DirA', reference: {} as Type}), host: {'[@prop]': 'expr'} @@ -360,14 +367,17 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'. it('should not issue a warning when an animation property is bound without an expression', () => { - humanizeTplAst(parse('
', [])); + humanizeTplAst(parse('
', [], [], [])); expect(console.warnings.length).toEqual(0); }); it('should parse bound properties via [@] and not report them as attributes', () => { - expect(humanizeTplAst(parse('
', []))).toEqual([ + expect(humanizeTplAst(parse('
', [], [], []))).toEqual([ [ElementAst, 'div'], - [BoundElementPropertyAst, PropertyBindingType.Animation, 'something', 'value2', null] + [ + BoundElementPropertyAst, PropertyBindingType.Animation, 'someAnimation', 'value2', + null + ] ]); }); }); diff --git a/modules/@angular/core/src/animation/animation_output.ts b/modules/@angular/core/src/animation/animation_output.ts deleted file mode 100644 index bde629b3c95e..000000000000 --- a/modules/@angular/core/src/animation/animation_output.ts +++ /dev/null @@ -1,10 +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 - */ -export class AnimationOutput { - constructor(public name: string, public phase: string, public fullPropertyName: string) {} -} diff --git a/modules/@angular/core/src/core_private_export.ts b/modules/@angular/core/src/core_private_export.ts index 6a356841dac4..980ec38066df 100644 --- a/modules/@angular/core/src/core_private_export.ts +++ b/modules/@angular/core/src/core_private_export.ts @@ -9,7 +9,6 @@ import {ANY_STATE as ANY_STATE_, DEFAULT_STATE as DEFAULT_STATE_, EMPTY_STATE as EMPTY_STATE_, FILL_STYLE_FLAG as FILL_STYLE_FLAG_} from './animation/animation_constants'; import {AnimationGroupPlayer as AnimationGroupPlayer_} from './animation/animation_group_player'; import {AnimationKeyframe as AnimationKeyframe_} from './animation/animation_keyframe'; -import {AnimationOutput as AnimationOutput_} from './animation/animation_output'; import {AnimationPlayer as AnimationPlayer_, NoOpAnimationPlayer as NoOpAnimationPlayer_} from './animation/animation_player'; import {AnimationSequencePlayer as AnimationSequencePlayer_} from './animation/animation_sequence_player'; import * as animationUtils from './animation/animation_style_util'; @@ -115,7 +114,6 @@ export var __core_private__: { renderStyles: typeof animationUtils.renderStyles, collectAndResolveStyles: typeof animationUtils.collectAndResolveStyles, AnimationStyles: typeof AnimationStyles_, _AnimationStyles?: AnimationStyles_, - AnimationOutput: typeof AnimationOutput_, _AnimationOutput?: AnimationOutput_, ANY_STATE: typeof ANY_STATE_, DEFAULT_STATE: typeof DEFAULT_STATE_, EMPTY_STATE: typeof EMPTY_STATE_, @@ -183,7 +181,6 @@ export var __core_private__: { renderStyles: animationUtils.renderStyles, collectAndResolveStyles: animationUtils.collectAndResolveStyles, AnimationStyles: AnimationStyles_, - AnimationOutput: AnimationOutput_, ANY_STATE: ANY_STATE_, DEFAULT_STATE: DEFAULT_STATE_, EMPTY_STATE: EMPTY_STATE_, diff --git a/modules/@angular/core/src/linker/view.ts b/modules/@angular/core/src/linker/view.ts index 198157628e00..23cae11b167a 100644 --- a/modules/@angular/core/src/linker/view.ts +++ b/modules/@angular/core/src/linker/view.ts @@ -7,7 +7,6 @@ */ import {AnimationGroupPlayer} from '../animation/animation_group_player'; -import {AnimationOutput} from '../animation/animation_output'; import {AnimationPlayer, NoOpAnimationPlayer} from '../animation/animation_player'; import {queueAnimation} from '../animation/animation_queue'; import {AnimationTransitionEvent} from '../animation/animation_transition_event'; @@ -53,7 +52,7 @@ export abstract class AppView { public animationPlayers = new ViewAnimationMap(); - private _animationListeners = new Map(); + private _animationListeners = new Map(); public context: T; @@ -107,7 +106,7 @@ export abstract class AppView { let listener = listeners[i]; // we check for both the name in addition to the phase in the event // that there may be more than one @trigger on the same element - if (listener.output.name == animationName && listener.output.phase == phase) { + if (listener.eventName === animationName && listener.eventPhase === phase) { listener.handler(event); break; } @@ -115,14 +114,13 @@ export abstract class AppView { } } - registerAnimationOutput(element: any, outputEvent: AnimationOutput, eventHandler: Function): - void { - var entry = new _AnimationOutputWithHandler(outputEvent, eventHandler); + registerAnimationOutput( + element: any, eventName: string, eventPhase: string, eventHandler: Function): void { var animations = this._animationListeners.get(element); if (!isPresent(animations)) { this._animationListeners.set(element, animations = []); } - animations.push(entry); + animations.push(new _AnimationOutputHandler(eventName, eventPhase, eventHandler)); } create(context: T, givenProjectableNodes: Array, rootSelectorOrNode: string|any): @@ -469,6 +467,6 @@ function _findLastRenderNode(node: any): any { return lastNode; } -class _AnimationOutputWithHandler { - constructor(public output: AnimationOutput, public handler: Function) {} +class _AnimationOutputHandler { + constructor(public eventName: string, public eventPhase: string, public handler: Function) {} } diff --git a/modules/@angular/core/test/animation/animation_integration_spec.ts b/modules/@angular/core/test/animation/animation_integration_spec.ts index 88a1f56cbe4a..622c0b3bc59b 100644 --- a/modules/@angular/core/test/animation/animation_integration_spec.ts +++ b/modules/@angular/core/test/animation/animation_integration_spec.ts @@ -997,7 +997,7 @@ function declareTests({useJit}: {useJit: boolean}) {
outer
- inner + inner <
<
`, @@ -1234,8 +1234,7 @@ function declareTests({useJit}: {useJit: boolean}) { message = e.message; } - expect(message).toMatch( - /- Couldn't find the corresponding animation trigger definition for \(@something\)/); + expect(message).toMatch(/Couldn't find an animation entry for "something"/); }); it('should throw an error if an animation output is referenced that is not bound to as a property on the same element', @@ -1258,7 +1257,7 @@ function declareTests({useJit}: {useJit: boolean}) { } expect(message).toMatch( - /- Unable to listen on \(@trigger.done\) because the animation trigger \[@trigger\] isn't being used on the same element/); + /Unable to listen on \(@trigger.done\) because the animation trigger \[@trigger\] isn't being used on the same element/); }); it('should throw an error if an unsupported animation output phase name is used', () => { @@ -1287,7 +1286,7 @@ function declareTests({useJit}: {useJit: boolean}) { TestBed.overrideComponent(DummyIfCmp, { set: { template: ` -
+
`, animations: [trigger('trigger', [transition('one => two', [animate(1000)])])] } @@ -1319,7 +1318,7 @@ function declareTests({useJit}: {useJit: boolean}) { } expect(message).toMatch( - /Couldn't find the corresponding host-level animation trigger definition for \(@trigger\)/); + /Unable to listen on \(@trigger.done\) because the animation trigger \[@trigger\] isn't being used on the same element/); }); it('should allow host and element-level animation bindings to be defined on the same tag/component', @@ -1480,11 +1479,27 @@ function declareTests({useJit}: {useJit: boolean}) { failureMessage = e.message; } - expect(failureMessage) - .toMatch(/Animation parsing for DummyIfCmp has failed due to the following errors:/); - expect(failureMessage).toMatch(/- Couldn't find an animation entry for status/); + expect(failureMessage).toMatch(/Template parse errors:/); + expect(failureMessage).toMatch(/Couldn't find an animation entry for "status"/); }); + it('should throw an error if an animation trigger is registered but is already in use', () => { + TestBed.overrideComponent( + DummyIfCmp, {set: {animations: [trigger('matias', []), trigger('matias', [])]}}); + + var failureMessage = ''; + try { + const fixture = TestBed.createComponent(DummyLoadingCmp); + } catch (e) { + failureMessage = e.message; + } + + expect(failureMessage).toMatch(/Animation parse errors:/); + expect(failureMessage) + .toMatch( + /The animation trigger "matias" has already been registered for the DummyIfCmp component/); + }); + it('should be permitted to be registered on the host element', fakeAsync(() => { TestBed.overrideComponent(DummyLoadingCmp, { set: { @@ -1521,7 +1536,7 @@ function declareTests({useJit}: {useJit: boolean}) { failureMessage = e.message; } - expect(failureMessage).toMatch(/- Couldn't find an animation entry for loading/); + expect(failureMessage).toMatch(/Couldn't find an animation entry for "loading"/); }); it('should retain the destination animation state styles once the animation is complete', From 5c4215ccb457280534b55c61aa4d9decce36b4a9 Mon Sep 17 00:00:00 2001 From: Flounn Date: Fri, 23 Sep 2016 22:44:01 +0200 Subject: [PATCH 19/70] Fix(http): invalidStateError if response body without content (#11786) Fix(http): invalidStateError if response body without content If the responseType has been specified and other than 'text', responseText throw an InvalidStateError exception See XHR doc => https://xhr.spec.whatwg.org/#the-responsetext-attribute Unit Test to prevent invalidStateError --- .../@angular/http/src/backends/xhr_backend.ts | 5 ++--- .../http/test/backends/xhr_backend_spec.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/modules/@angular/http/src/backends/xhr_backend.ts b/modules/@angular/http/src/backends/xhr_backend.ts index 416cf552b348..90d4f3291244 100644 --- a/modules/@angular/http/src/backends/xhr_backend.ts +++ b/modules/@angular/http/src/backends/xhr_backend.ts @@ -54,9 +54,8 @@ export class XHRConnection implements Connection { let onLoad = () => { // responseText is the old-school way of retrieving response (supported by IE8 & 9) // response/responseType properties were introduced in ResourceLoader Level2 spec (supported - // by - // IE10) - let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText; + // by IE10) + let body = _xhr.response === undefined ? _xhr.responseText : _xhr.response; // Implicitly strip a potential XSSI prefix. if (isString(body)) body = body.replace(XSSI_PREFIX, ''); let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders()); diff --git a/modules/@angular/http/test/backends/xhr_backend_spec.ts b/modules/@angular/http/test/backends/xhr_backend_spec.ts index 78cb2bec6070..0b74c4b14a43 100644 --- a/modules/@angular/http/test/backends/xhr_backend_spec.ts +++ b/modules/@angular/http/test/backends/xhr_backend_spec.ts @@ -686,6 +686,23 @@ Connection: keep-alive`; existingXHRs[0].setStatusCode(statusCode); existingXHRs[0].dispatchEvent('load'); })); + + it('should not throw invalidStateError if response without body and responseType not equal to text', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const base = new BaseRequestOptions(); + const connection = new XHRConnection( + new Request( + base.merge(new RequestOptions({responseType: ResponseContentType.Json}))), + new MockBrowserXHR()); + + connection.response.subscribe((res: Response) => { + expect(res.json()).toBe(null); + async.done(); + }); + + existingXHRs[0].setStatusCode(204); + existingXHRs[0].dispatchEvent('load'); + })); }); }); } From 6d606dd02bc6a8471141aab68eba96375d726493 Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Fri, 23 Sep 2016 14:04:29 -0700 Subject: [PATCH 20/70] ci(travis): increase node's heap size to prevent OOM on travis (#11869) --- scripts/ci-lite/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci-lite/build.sh b/scripts/ci-lite/build.sh index 1c6f2f91ab2a..5510b9e1dc9f 100755 --- a/scripts/ci-lite/build.sh +++ b/scripts/ci-lite/build.sh @@ -12,7 +12,7 @@ cd ../.. $(npm bin)/tsc -v $(npm bin)/tsc -p tools cp tools/@angular/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped -node dist/tools/@angular/tsc-wrapped/src/main -p modules +node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main -p modules node dist/tools/@angular/tsc-wrapped/src/main -p modules/@angular/core/tsconfig-build.json node dist/tools/@angular/tsc-wrapped/src/main -p modules/@angular/common/tsconfig-build.json node dist/tools/@angular/tsc-wrapped/src/main -p modules/@angular/router/tsconfig-build.json From 0a8887240abb97cfed86e02990021518a986fe4d Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 23 Sep 2016 15:05:43 -0700 Subject: [PATCH 21/70] docs(ExceptionHandler): fix API docs (#11772) fixes #11769 --- modules/@angular/core/src/error_handler.ts | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/modules/@angular/core/src/error_handler.ts b/modules/@angular/core/src/error_handler.ts index f9c3228d92ca..35222a225b42 100644 --- a/modules/@angular/core/src/error_handler.ts +++ b/modules/@angular/core/src/error_handler.ts @@ -9,18 +9,19 @@ import {WrappedError} from './facade/errors'; /** - * Provides a hook for centralized exception handling. + * @whatItDoes Provides a hook for centralized exception handling. * - * The default implementation of `ErrorHandler` prints error messages to the `Console`. To - * intercept error handling, - * write a custom exception handler that replaces this default as appropriate for your app. + * @description * - * ### Example + * The default implementation of `ErrorHandler` prints error messages to the `console`. To + * intercept error handling, write a custom exception handler that replaces this default as + * appropriate for your app. * - * ```javascript + * ### Example * + * ``` * class MyErrorHandler implements ErrorHandler { - * call(error, stackTrace = null, reason = null) { + * handleError(error) { * // do something with the exception * } * } @@ -30,6 +31,7 @@ import {WrappedError} from './facade/errors'; * }) * class MyModule {} * ``` + * * @stable */ export class ErrorHandler { @@ -46,9 +48,9 @@ export class ErrorHandler { constructor(rethrowError: boolean = true) { this.rethrowError = rethrowError; } handleError(error: any): void { - var originalError = this._findOriginalError(error); - var originalStack = this._findOriginalStack(error); - var context = this._findContext(error); + const originalError = this._findOriginalError(error); + const originalStack = this._findOriginalStack(error); + const context = this._findContext(error); this._console.error(`EXCEPTION: ${this._extractMessage(error)}`); @@ -81,14 +83,14 @@ export class ErrorHandler { if (error) { return error.context ? error.context : this._findContext((error as WrappedError).originalError); - } else { - return null; } + + return null; } /** @internal */ _findOriginalError(error: any): any { - var e = (error as WrappedError).originalError; + let e = (error as WrappedError).originalError; while (e && (e as WrappedError).originalError) { e = (e as WrappedError).originalError; } @@ -100,8 +102,8 @@ export class ErrorHandler { _findOriginalStack(error: any): string { if (!(error instanceof Error)) return null; - var e: any = error; - var stack: string = e.stack; + let e: any = error; + let stack: string = e.stack; while (e instanceof Error && (e as WrappedError).originalError) { e = (e as WrappedError).originalError; if (e instanceof Error && e.stack) { From f633826e9943282e3454a7472cd65abdcfafed02 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sat, 24 Sep 2016 07:13:24 +0900 Subject: [PATCH 22/70] chore(CHANGELOG): fix wrong issue link (#11871) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b42b46a148..77d380efb08e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ * **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652)) * **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590) * **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643) -* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#1645](https://github.com/angular/angular/issues/1645) +* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#11645](https://github.com/angular/angular/issues/11645) * **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418)) * **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719) * **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e)) From b8a75818ee09a79eae52a7316164034d11a954bd Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 23 Sep 2016 16:23:28 -0700 Subject: [PATCH 23/70] docs: remove outdated docs (#11875) --- .../change_detection/change_detector_ref.ts | 6 +- modules/@angular/core/src/linker/errors.ts | 9 +- .../@angular/core/src/metadata/directives.ts | 9 +- modules/@angular/core/src/zone/ng_zone.ts | 1 - .../docs/change_detection/change_detection.md | 10 - .../docs/change_detection/expressions.md | 7 - .../@angular/docs/change_detection/record.md | 3 - modules/@angular/docs/core/00_index.md | 3 - modules/@angular/docs/core/01_templates.md | 610 ------------------ modules/@angular/docs/core/02_directives.md | 378 ----------- modules/@angular/docs/core/03_pipes.md | 27 - .../docs/core/04_decorator_directive.md | 0 .../docs/core/05_component_directive.md | 5 - .../docs/core/06_viewport_directive.md | 0 modules/@angular/docs/core/07_services.md | 0 modules/@angular/docs/core/08_lifecycle.md | 0 modules/@angular/docs/core/09_compilation.md | 21 - modules/@angular/docs/core/10_view.md | 246 ------- modules/@angular/docs/core/11_shadow_dom.md | 0 modules/@angular/docs/core/12_zones.md | 108 ---- modules/@angular/docs/migration/kebab-case.md | 239 ------- modules/@angular/forms/src/directives.ts | 15 +- 22 files changed, 8 insertions(+), 1689 deletions(-) delete mode 100644 modules/@angular/docs/change_detection/change_detection.md delete mode 100644 modules/@angular/docs/change_detection/expressions.md delete mode 100644 modules/@angular/docs/change_detection/record.md delete mode 100644 modules/@angular/docs/core/00_index.md delete mode 100644 modules/@angular/docs/core/01_templates.md delete mode 100644 modules/@angular/docs/core/02_directives.md delete mode 100644 modules/@angular/docs/core/03_pipes.md delete mode 100644 modules/@angular/docs/core/04_decorator_directive.md delete mode 100644 modules/@angular/docs/core/05_component_directive.md delete mode 100644 modules/@angular/docs/core/06_viewport_directive.md delete mode 100644 modules/@angular/docs/core/07_services.md delete mode 100644 modules/@angular/docs/core/08_lifecycle.md delete mode 100644 modules/@angular/docs/core/09_compilation.md delete mode 100644 modules/@angular/docs/core/10_view.md delete mode 100644 modules/@angular/docs/core/11_shadow_dom.md delete mode 100644 modules/@angular/docs/core/12_zones.md delete mode 100644 modules/@angular/docs/migration/kebab-case.md diff --git a/modules/@angular/core/src/change_detection/change_detector_ref.ts b/modules/@angular/core/src/change_detection/change_detector_ref.ts index e91a7cd5734e..21bb7dde3885 100644 --- a/modules/@angular/core/src/change_detection/change_detector_ref.ts +++ b/modules/@angular/core/src/change_detection/change_detector_ref.ts @@ -41,7 +41,6 @@ export abstract class ChangeDetectorRef { * template: ` * * `, - * directives: [Cmp] * }) * class App { * } @@ -81,7 +80,6 @@ export abstract class ChangeDetectorRef { * template: ` *
  • Data {{d}} * `, - * directives: [NgFor] * }) * class GiantList { * constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) { @@ -98,7 +96,6 @@ export abstract class ChangeDetectorRef { * template: ` * * `, - * directives: [GiantList] * }) * class App { * } @@ -166,7 +163,7 @@ export abstract class ChangeDetectorRef { * @Component({ * selector: 'live-data', * inputs: ['live'], - * template: `Data: {{dataProvider.data}}` + * template: 'Data: {{dataProvider.data}}' * }) * class LiveData { * constructor(private ref: ChangeDetectorRef, private dataProvider:DataProvider) {} @@ -186,7 +183,6 @@ export abstract class ChangeDetectorRef { * Live Update: * * `, - * directives: [LiveData, FORM_DIRECTIVES] * }) * class App { * live = true; diff --git a/modules/@angular/core/src/linker/errors.ts b/modules/@angular/core/src/linker/errors.ts index 2154076327e7..704bc1addf15 100644 --- a/modules/@angular/core/src/linker/errors.ts +++ b/modules/@angular/core/src/linker/errors.ts @@ -25,13 +25,10 @@ import {DebugContext} from './debug_context'; * ```typescript * @Component({ * selector: 'parent', - * template: ` - * - * `, - * directives: [forwardRef(() => Child)] + * template: '', * }) * class Parent { - * parentProp = "init"; + * parentProp = 'init'; * } * * @Directive({selector: 'child', inputs: ['prop']}) @@ -41,7 +38,7 @@ import {DebugContext} from './debug_context'; * set prop(v) { * // this updates the parent property, which is disallowed during change detection * // this will result in ExpressionChangedAfterItHasBeenCheckedError - * this.parent.parentProp = "updated"; + * this.parent.parentProp = 'updated'; * } * } * ``` diff --git a/modules/@angular/core/src/metadata/directives.ts b/modules/@angular/core/src/metadata/directives.ts index 7a1cf43becd8..9fc49cf5eb65 100644 --- a/modules/@angular/core/src/metadata/directives.ts +++ b/modules/@angular/core/src/metadata/directives.ts @@ -928,7 +928,6 @@ export interface HostBindingDecorator { * @Component({ * selector: 'app', * template: ``, - * directives: [FORM_DIRECTIVES, NgModelStatus] * }) * class App { * prop; @@ -968,8 +967,7 @@ export interface HostListenerDecorator { * * Angular will invoke the decorated method when the host element emits the specified event. * - * If the decorated method returns `false`, then `preventDefault` is applied on the DOM - * event. + * If the decorated method returns `false`, then `preventDefault` is applied on the DOM event. * * ### Example * @@ -983,14 +981,13 @@ export interface HostListenerDecorator { * * @HostListener('click', ['$event.target']) * onClick(btn) { - * console.log("button", btn, "number of clicks:", this.numberOfClicks++); + * console.log('button', btn, 'number of clicks:', this.numberOfClicks++); * } * } * * @Component({ * selector: 'app', - * template: ``, - * directives: [CountClicks] + * template: '', * }) * class App {} * ``` diff --git a/modules/@angular/core/src/zone/ng_zone.ts b/modules/@angular/core/src/zone/ng_zone.ts index c4afd6501ffd..6f90f722fe54 100644 --- a/modules/@angular/core/src/zone/ng_zone.ts +++ b/modules/@angular/core/src/zone/ng_zone.ts @@ -40,7 +40,6 @@ import {NgZoneImpl} from './ng_zone_impl'; * * * `, - * directives: [NgIf] * }) * export class NgZoneDemo { * progress: number = 0; diff --git a/modules/@angular/docs/change_detection/change_detection.md b/modules/@angular/docs/change_detection/change_detection.md deleted file mode 100644 index 4207687163db..000000000000 --- a/modules/@angular/docs/change_detection/change_detection.md +++ /dev/null @@ -1,10 +0,0 @@ -@name Change Detection -@description -# Change Detection - -* Mechanisms by which changes are detected in the model -* DAG -* Order of evaluation -* Pure Functions -* `onChange` method -* Parser diff --git a/modules/@angular/docs/change_detection/expressions.md b/modules/@angular/docs/change_detection/expressions.md deleted file mode 100644 index e9cdc9aa9bb6..000000000000 --- a/modules/@angular/docs/change_detection/expressions.md +++ /dev/null @@ -1,7 +0,0 @@ -# Expressions - -## Binding Semantics - -### Formatters - -## Statement Semantics \ No newline at end of file diff --git a/modules/@angular/docs/change_detection/record.md b/modules/@angular/docs/change_detection/record.md deleted file mode 100644 index 343624acec22..000000000000 --- a/modules/@angular/docs/change_detection/record.md +++ /dev/null @@ -1,3 +0,0 @@ -# Record - -## RecordRange \ No newline at end of file diff --git a/modules/@angular/docs/core/00_index.md b/modules/@angular/docs/core/00_index.md deleted file mode 100644 index 95c575cf1b0b..000000000000 --- a/modules/@angular/docs/core/00_index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Overview - -* High level description of all of the components. \ No newline at end of file diff --git a/modules/@angular/docs/core/01_templates.md b/modules/@angular/docs/core/01_templates.md deleted file mode 100644 index abd6d2cc668b..000000000000 --- a/modules/@angular/docs/core/01_templates.md +++ /dev/null @@ -1,610 +0,0 @@ -# Templates - -Templates are markup which is added to HTML to declaratively describe how the application model should be -projected to DOM as well as which DOM events should invoke which methods on the controller. Templates contain -syntaxes which are core to Angular and allows for data-binding, event-binding, template-instantiation. - -The design of the template syntax has these properties: - - -* All data-binding expressions are easily identifiable. (i.e. there is never an ambiguity whether the value should be - interpreted as string literal or as an expression.) -* All events and their statements are easily identifiable. -* All places of DOM instantiation are easily identifiable. -* All places of variable declaration are easily identifiable. - -The above properties guarantee that the templates are easy to parse by tools (such as IDEs) and reason about by people. -At no point is it necessary to understand which directives are active or what their semantics are in order to reason -about the template runtime characteristics. - - - -## Summary - -Below is a summary of the kinds of syntaxes which Angular templating supports. The syntaxes are explained in more -detail in the following sections. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    DescriptionShortCanonical
    Text Interpolation -
    -<div>{{exp}}</div>
    -
    - -Example: -
    -<div>
    -  Hello {{name}}!
    -  <br>
    -  Goodbye {{name}}!
    -</div>
    -
    -
    -
    -<div [text|index]="exp"></div>
    -
    - -Example: -
    -<div
    -  [text|0]=" 'Hello' + stringify(name) + '!' "
    -  [text|2]=" 'Goodbye' + stringify(name) + '!' ">
    -  <b>x</b>
    -</div>
    -
    -
    Property Interpolation -
    -<div name="{{exp}}"></div>
    -
    - -Example: - -
    -<div class="{{selected}}"></div>
    -
    -
    -
    -<div [name]="stringify(exp)"></div>
    -
    - -Example: - -
    -<div [title]="stringify(selected)"></div>
    -
    -
    Property binding -
    -<div [prop]="exp"></div>
    -
    - -Example: - -
    -<div [hidden]="true"></div>
    -
    -
    -
    -<div bind-prop="exp"></div>
    -
    - -Example: - -
    -<div bind-hidden="true"></div>
    -
    -
    Event binding (non-bubbling) -
    -<div (event)="statement"></div>
    -
    - -Example: - -
    -<div (click)="doX()"></div>
    -
    -
    -
    -<div on-event="statement"></div>
    -
    - -Example: - -
    -<video #player>
    -  <button (click)="player.play()">play</button>
    -</video>
    -
    - -Or: - -
    -<div def="symbol"></div>
    -
    - -Example: - -
    -<video def="player">
    -  <button on-click="player.play()">play</button>
    -</video>
    -
    -
    Inline Template -
    -<div template="...">...</div>
    -
    - -Example: - -
    -<ul>
    -  <li template="for: #item of items">
    -    {{item}}
    -  </li>
    -</ul>
    -
    -
    -
    -<template>...</template>
    -
    - -Example: -
    -<ul>
    -  <template def-for:"item"
    -            bind-for-in="items">
    -    <li>
    -      {{item}}
    -    </li>
    -  </template>
    -</ul>
    -
    -
    Explicit Template -
    -<template>...</template>
    -
    - -Example: - -
    -<template #for="item"
    -          [for-in]="items">
    -  _some_content_to_repeat_
    -</template>
    -
    -
    -
    -<template>...</template>
    -
    - -Example: - -
    -<template def-for="item"
    -          bind-for-in="items">
    -  _some_content_to_repeat_
    -</template>
    -
    -
    - - - -## Property Binding - -Binding application model data to the UI is the most common kind of bindings in an Angular application. The bindings -are always in the form of `property-name` which is assigned an `expression`. The generic form is: - - - - - - - - - - -
    Short form
    <some-element [someProperty]="expression">
    Canonical form
    <some-element bind-some-property="expression">
    - - -Where: -* `some-element` can be any existing DOM element. -* `someProperty` or `some-property` (escaped with `[]` or `bind-`) is the name of the property on `some-element`. If - the property is dash-case, it will be converted into camel-case `someProperty`. -* `expression` is a valid expression (as defined in section below). - -Example: -``` -
    -``` - -In the above example the `title` property of the `div` element will be updated whenever the `user.firstName` changes -its value. - -Key points: -* The binding is to the element property not the element attribute. -* To prevent custom element from accidentally reading the literal `expression` on the title element, the attribute name - is escaped. In our case the `title` is escaped to `[title]` through the addition of square brackets `[]`. -* A binding value (in this case `user.firstName`) will always be an expression, never a string literal. - -NOTE: Unlike Angular v1, Angular v2 binds to properties of elements rather than attributes of elements. This is -done to better support custom elements, and to allow binding for values other than strings. - -NOTE: Some editors/server side pre-processors may have trouble generating `[]` around the attribute name. For this -reason Angular also supports a canonical version which is prefixed using `bind-`. - - - -### String Interpolation - -Property bindings are the only data bindings which Angular supports, but for convenience Angular supports an interpolation -syntax which is just a short hand for the data binding syntax. - -``` -Hello {{name}}! -``` - -is a short hand for: - -``` - -``` - -The above says to bind the `'Hello ' + stringify(name) + '!'` expression to the zero-th child of the `span`'s `text` -property. The index is necessary in case there are more than one text nodes, or if the text node we wish to bind to -is not the first one. - -Similarly the same rules apply to interpolation inside attributes. - -``` - -``` - -is a short hand for: - -``` - -``` - -NOTE: `stringify()` is a built in implicit function which converts its argument to a string representation, while -keeping `null` and `undefined` as empty strings. - - - - -## Local Variables - - - - -## Inline Templates - -Data binding allows updating the DOM's properties, but it does not allow for changing of the DOM structure. To change -DOM structure we need the ability to define child templates, and then instantiate these templates into Views. The -Views than can be inserted and removed as needed to change the DOM structure. - - - - - - - - - - -
    Short form -
    -parent template
    -<element>
    -  <some-element template="instantiating-directive-microsyntax">child template</some-element>
    -</element>
    -
    -
    Canonical form -
    -parent template
    -<element>
    -  <template instantiating-directive-bindings>
    -    <some-element>child template</some-element>
    -  </template>
    -</element>
    -
    -
    - -Where: -* `template` defines a child template and designates the anchor where Views (instances of the template) will be - inserted. The template can be defined implicitly with `template` attribute, which turns the current element into - a template, or explicitly with `