diff --git a/build.sh b/build.sh index a9a9bad088..7b373e0aba 100755 --- a/build.sh +++ b/build.sh @@ -44,6 +44,7 @@ for PACKAGE in \ core \ compiler \ common \ + forms \ platform-browser \ platform-browser-dynamic \ platform-server \ diff --git a/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts b/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts index 71f2773f7f..9072bacdc0 100644 --- a/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts +++ b/modules/@angular/common/src/forms-deprecated/directives/ng_form.ts @@ -95,6 +95,11 @@ export class NgForm extends ControlContainer implements Form { @Optional() @Self() @Inject(NG_VALIDATORS) validators: any[], @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: any[]) { super(); + console.warn(` + *It looks like you're using the old forms module. This will be opt-in in the next RC, and + will eventually be removed in favor of the new forms module. For more information, see: + https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub + `); this.form = new ControlGroup( {}, null, composeValidators(validators), composeAsyncValidators(asyncValidators)); } diff --git a/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts b/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts index 30b0669280..8c358b141c 100644 --- a/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts +++ b/modules/@angular/common/src/forms-deprecated/directives/ng_form_model.ts @@ -113,6 +113,11 @@ export class NgFormModel extends ControlContainer implements Form, @Optional() @Self() @Inject(NG_VALIDATORS) private _validators: any[], @Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) private _asyncValidators: any[]) { super(); + console.warn(` + *It looks like you're using the old forms module. This will be opt-in in the next RC, and + will eventually be removed in favor of the new forms module. For more information, see: + https://docs.google.com/document/u/1/d/1RIezQqE4aEhBRmArIAS1mRIZtWFf6JxN_7B4meyWK0Y/pub + `); } ngOnChanges(changes: SimpleChanges): void { diff --git a/modules/@angular/common/src/forms.ts b/modules/@angular/common/src/forms.ts deleted file mode 100644 index 8333300ac7..0000000000 --- a/modules/@angular/common/src/forms.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @module - * @description - * This module is used for handling user input, by defining and building a {@link FormGroup} that - * consists of - * {@link FormControl} objects, and mapping them onto the DOM. {@link FormControl} objects can then - * be used - * to read information - * from the form DOM elements. - * - * Forms providers are not included in default providers; you must import these providers - * explicitly. - */ -import {Type} from '@angular/core'; - -import {RadioControlRegistry} from './forms/directives/radio_control_value_accessor'; -import {FormBuilder} from './forms/form_builder'; - -export {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, RadioButtonState} from './forms/directives'; -export {AbstractControlDirective} from './forms/directives/abstract_control_directive'; -export {CheckboxControlValueAccessor} from './forms/directives/checkbox_value_accessor'; -export {ControlContainer} from './forms/directives/control_container'; -export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './forms/directives/control_value_accessor'; -export {DefaultValueAccessor} from './forms/directives/default_value_accessor'; -export {Form} from './forms/directives/form_interface'; -export {NgControl} from './forms/directives/ng_control'; -export {NgControlStatus} from './forms/directives/ng_control_status'; -export {NgForm} from './forms/directives/ng_form'; -export {NgModel} from './forms/directives/ng_model'; -export {NgModelGroup} from './forms/directives/ng_model_group'; -export {FormControlDirective} from './forms/directives/reactive_directives/form_control_directive'; -export {FormControlName} from './forms/directives/reactive_directives/form_control_name'; -export {FormGroupDirective} from './forms/directives/reactive_directives/form_group_directive'; -export {FormGroupName} from './forms/directives/reactive_directives/form_group_name'; -export {NgSelectOption, SelectControlValueAccessor} from './forms/directives/select_control_value_accessor'; -export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './forms/directives/validators'; -export {FormBuilder} from './forms/form_builder'; -export {AbstractControl, FormArray, FormControl, FormGroup} from './forms/model'; -export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './forms/validators'; - - - -/** - * Shorthand set of providers used for building Angular forms. - * - * ### Example - * - * ```typescript - * bootstrap(MyApp, [FORM_PROVIDERS]); - * ``` - * - * @experimental - */ -export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[FormBuilder, RadioControlRegistry]; diff --git a/modules/@angular/core/core.dart b/modules/@angular/core/core.dart index 28f59a9307..fe0cc13dd1 100644 --- a/modules/@angular/core/core.dart +++ b/modules/@angular/core/core.dart @@ -21,6 +21,7 @@ export './src/core/debug/debug_node.dart' show DebugElement, asNativeElements; export './src/core/testability/testability.dart'; export './src/core/change_detection.dart'; +export './src/core/platform_directives_and_pipes.dart'; export './src/core/platform_common_providers.dart'; export './src/core/application_common_providers.dart'; export './src/core/reflection/reflection.dart'; diff --git a/modules/@angular/core/index.ts b/modules/@angular/core/index.ts index 5f4b02349f..f4d4a25f22 100644 --- a/modules/@angular/core/index.ts +++ b/modules/@angular/core/index.ts @@ -14,6 +14,7 @@ export * from './src/linker'; export {DebugElement, DebugNode, asNativeElements, getDebugNode} from './src/debug/debug_node'; export * from './src/testability/testability'; export * from './src/change_detection'; +export * from './src/platform_directives_and_pipes'; export * from './src/platform_common_providers'; export * from './src/application_common_providers'; export {wtfCreateScope, wtfLeave, wtfStartTimeRange, wtfEndTimeRange, WtfScopeFn} from './src/profile/profile'; diff --git a/modules/@angular/core/src/platform_directives_and_pipes.ts b/modules/@angular/core/src/platform_directives_and_pipes.ts new file mode 100644 index 0000000000..03974185ec --- /dev/null +++ b/modules/@angular/core/src/platform_directives_and_pipes.ts @@ -0,0 +1,58 @@ +import {OpaqueToken} from './di'; + +/** + A token that can be provided when bootstrapping an application to make an array of directives + * available in every component of the application. + * + * ### Example + * + * ```typescript + * import {PLATFORM_DIRECTIVES} from '@angular/core'; + * import {OtherDirective} from './myDirectives'; + * + * @Component({ + * selector: 'my-component', + * template: ` + * + * + * ` + * }) + * export class MyComponent { + * ... + * } + * + * bootstrap(MyComponent, [{provide: PLATFORM_DIRECTIVES, useValue: [OtherDirective], + multi:true}]); + * ``` + * @stable + */ + +export const PLATFORM_DIRECTIVES: OpaqueToken = + /*@ts2dart_const*/ new OpaqueToken('Platform Directives'); + +/** + * A token that can be provided when bootstraping an application to make an array of pipes + * available in every component of the application. + * + * ### Example + * + * ```typescript + * import {PLATFORM_PIPES} from '@angular/core'; + * import {OtherPipe} from './myPipe'; + * + * @Component({ + * selector: 'my-component', + * template: ` + * {{123 | other-pipe}} + * ` + * }) + * export class MyComponent { + * ... + * } + * + * bootstrap(MyComponent, [{provide: PLATFORM_PIPES, useValue: [OtherPipe], multi:true}]); + * ``` + * @stable + */ + +export const PLATFORM_PIPES: OpaqueToken = /*@ts2dart_const*/ new OpaqueToken('Platform Pipes'); \ No newline at end of file diff --git a/modules/@angular/forms/common.dart b/modules/@angular/forms/common.dart new file mode 100644 index 0000000000..1592d948a1 --- /dev/null +++ b/modules/@angular/forms/common.dart @@ -0,0 +1 @@ +export 'index.dart'; diff --git a/modules/@angular/forms/index.ts b/modules/@angular/forms/index.ts new file mode 100644 index 0000000000..17858b1182 --- /dev/null +++ b/modules/@angular/forms/index.ts @@ -0,0 +1 @@ +export * from './src/forms'; diff --git a/modules/@angular/forms/package.json b/modules/@angular/forms/package.json new file mode 100644 index 0000000000..2448d234fd --- /dev/null +++ b/modules/@angular/forms/package.json @@ -0,0 +1,19 @@ +{ + "name": "@angular/forms", + "version": "0.0.0-PLACEHOLDER", + "description": "", + "main": "index.js", + "jsnext:main": "esm/index.js", + "typings": "index.d.ts", + "author": "angular", + "license": "MIT", + "peerDependencies": { + "@angular/core": "0.0.0-PLACEHOLDER", + "@angular/common": "0.0.0-PLACEHOLDER", + "@angular/compiler": "0.0.0-PLACEHOLDER" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.git" + } +} diff --git a/modules/@angular/forms/rollup.config.js b/modules/@angular/forms/rollup.config.js new file mode 100644 index 0000000000..2e44f38fa5 --- /dev/null +++ b/modules/@angular/forms/rollup.config.js @@ -0,0 +1,19 @@ + +export default { + entry: '../../../dist/packages-dist/forms/esm/index.js', + dest: '../../../dist/packages-dist/forms/esm/forms.umd.js', + format: 'umd', + moduleName: 'ng.forms', + globals: { + '@angular/core': 'ng.core', + '@angular/common': 'ng.common', + '@angular/compiler': 'ng.compiler', + 'rxjs/Subject': 'Rx', + 'rxjs/observable/PromiseObservable': 'Rx', // this is wrong, but this stuff has changed in rxjs b.6 so we need to fix it when we update. + 'rxjs/operator/toPromise': 'Rx.Observable.prototype', + 'rxjs/Observable': 'Rx' + }, + plugins: [ +// nodeResolve({ jsnext: true, main: true }), + ] +} diff --git a/modules/@angular/common/src/forms/directives.ts b/modules/@angular/forms/src/directives.ts similarity index 100% rename from modules/@angular/common/src/forms/directives.ts rename to modules/@angular/forms/src/directives.ts diff --git a/modules/@angular/common/src/forms/directives/abstract_control_directive.ts b/modules/@angular/forms/src/directives/abstract_control_directive.ts similarity index 90% rename from modules/@angular/common/src/forms/directives/abstract_control_directive.ts rename to modules/@angular/forms/src/directives/abstract_control_directive.ts index 19155a3551..abd0444e1a 100644 --- a/modules/@angular/common/src/forms/directives/abstract_control_directive.ts +++ b/modules/@angular/forms/src/directives/abstract_control_directive.ts @@ -1,5 +1,5 @@ -import {unimplemented} from '../../facade/exceptions'; -import {isPresent} from '../../facade/lang'; +import {unimplemented} from '../facade/exceptions'; +import {isPresent} from '../facade/lang'; import {AbstractControl} from '../model'; diff --git a/modules/@angular/common/src/forms/directives/abstract_form_group_directive.ts b/modules/@angular/forms/src/directives/abstract_form_group_directive.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/abstract_form_group_directive.ts rename to modules/@angular/forms/src/directives/abstract_form_group_directive.ts diff --git a/modules/@angular/common/src/forms/directives/checkbox_value_accessor.ts b/modules/@angular/forms/src/directives/checkbox_value_accessor.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/checkbox_value_accessor.ts rename to modules/@angular/forms/src/directives/checkbox_value_accessor.ts diff --git a/modules/@angular/common/src/forms/directives/control_container.ts b/modules/@angular/forms/src/directives/control_container.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/control_container.ts rename to modules/@angular/forms/src/directives/control_container.ts diff --git a/modules/@angular/common/src/forms/directives/control_value_accessor.ts b/modules/@angular/forms/src/directives/control_value_accessor.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/control_value_accessor.ts rename to modules/@angular/forms/src/directives/control_value_accessor.ts diff --git a/modules/@angular/common/src/forms/directives/default_value_accessor.ts b/modules/@angular/forms/src/directives/default_value_accessor.ts similarity index 97% rename from modules/@angular/common/src/forms/directives/default_value_accessor.ts rename to modules/@angular/forms/src/directives/default_value_accessor.ts index 0bd476bcbd..fe7fbeb1ca 100644 --- a/modules/@angular/common/src/forms/directives/default_value_accessor.ts +++ b/modules/@angular/forms/src/directives/default_value_accessor.ts @@ -1,6 +1,6 @@ import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core'; -import {isBlank} from '../../facade/lang'; +import {isBlank} from '../facade/lang'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; diff --git a/modules/@angular/common/src/forms/directives/form_interface.ts b/modules/@angular/forms/src/directives/form_interface.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/form_interface.ts rename to modules/@angular/forms/src/directives/form_interface.ts diff --git a/modules/@angular/common/src/forms/directives/ng_control.ts b/modules/@angular/forms/src/directives/ng_control.ts similarity index 92% rename from modules/@angular/common/src/forms/directives/ng_control.ts rename to modules/@angular/forms/src/directives/ng_control.ts index e9d237b307..b9a8522773 100644 --- a/modules/@angular/common/src/forms/directives/ng_control.ts +++ b/modules/@angular/forms/src/directives/ng_control.ts @@ -1,4 +1,4 @@ -import {unimplemented} from '../../facade/exceptions'; +import {unimplemented} from '../facade/exceptions'; import {AbstractControlDirective} from './abstract_control_directive'; import {ControlValueAccessor} from './control_value_accessor'; diff --git a/modules/@angular/common/src/forms/directives/ng_control_status.ts b/modules/@angular/forms/src/directives/ng_control_status.ts similarity index 96% rename from modules/@angular/common/src/forms/directives/ng_control_status.ts rename to modules/@angular/forms/src/directives/ng_control_status.ts index 38627e246c..f5ba580bc0 100644 --- a/modules/@angular/common/src/forms/directives/ng_control_status.ts +++ b/modules/@angular/forms/src/directives/ng_control_status.ts @@ -1,6 +1,6 @@ import {Directive, Self} from '@angular/core'; -import {isPresent} from '../../facade/lang'; +import {isPresent} from '../facade/lang'; import {NgControl} from './ng_control'; diff --git a/modules/@angular/common/src/forms/directives/ng_form.ts b/modules/@angular/forms/src/directives/ng_form.ts similarity index 97% rename from modules/@angular/common/src/forms/directives/ng_form.ts rename to modules/@angular/forms/src/directives/ng_form.ts index 3ada4e4a4e..37c40d3b22 100644 --- a/modules/@angular/common/src/forms/directives/ng_form.ts +++ b/modules/@angular/forms/src/directives/ng_form.ts @@ -1,8 +1,8 @@ import {Directive, Inject, Optional, Self, forwardRef} from '@angular/core'; -import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../../facade/async'; -import {ListWrapper} from '../../facade/collection'; -import {isPresent} from '../../facade/lang'; +import {EventEmitter, ObservableWrapper, PromiseWrapper} from '../facade/async'; +import {ListWrapper} from '../facade/collection'; +import {isPresent} from '../facade/lang'; import {AbstractControl, FormControl, FormGroup} from '../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; diff --git a/modules/@angular/common/src/forms/directives/ng_model.ts b/modules/@angular/forms/src/directives/ng_model.ts similarity index 97% rename from modules/@angular/common/src/forms/directives/ng_model.ts rename to modules/@angular/forms/src/directives/ng_model.ts index a4a5fb19a6..2e125dd714 100644 --- a/modules/@angular/common/src/forms/directives/ng_model.ts +++ b/modules/@angular/forms/src/directives/ng_model.ts @@ -1,7 +1,7 @@ import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; -import {EventEmitter, ObservableWrapper} from '../../facade/async'; -import {BaseException} from '../../facade/exceptions'; +import {EventEmitter, ObservableWrapper} from '../facade/async'; +import {BaseException} from '../facade/exceptions'; import {FormControl} from '../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../validators'; diff --git a/modules/@angular/common/src/forms/directives/ng_model_group.ts b/modules/@angular/forms/src/directives/ng_model_group.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/ng_model_group.ts rename to modules/@angular/forms/src/directives/ng_model_group.ts diff --git a/modules/@angular/common/src/forms/directives/normalize_validator.dart b/modules/@angular/forms/src/directives/normalize_validator.dart similarity index 100% rename from modules/@angular/common/src/forms/directives/normalize_validator.dart rename to modules/@angular/forms/src/directives/normalize_validator.dart diff --git a/modules/@angular/common/src/forms/directives/normalize_validator.ts b/modules/@angular/forms/src/directives/normalize_validator.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/normalize_validator.ts rename to modules/@angular/forms/src/directives/normalize_validator.ts diff --git a/modules/@angular/common/src/forms/directives/number_value_accessor.ts b/modules/@angular/forms/src/directives/number_value_accessor.ts similarity index 96% rename from modules/@angular/common/src/forms/directives/number_value_accessor.ts rename to modules/@angular/forms/src/directives/number_value_accessor.ts index 58b37d9161..f0910df337 100644 --- a/modules/@angular/common/src/forms/directives/number_value_accessor.ts +++ b/modules/@angular/forms/src/directives/number_value_accessor.ts @@ -1,6 +1,6 @@ import {Directive, ElementRef, Renderer, forwardRef} from '@angular/core'; -import {NumberWrapper} from '../../facade/lang'; +import {NumberWrapper} from '../facade/lang'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; diff --git a/modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts b/modules/@angular/forms/src/directives/radio_control_value_accessor.ts similarity index 97% rename from modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts rename to modules/@angular/forms/src/directives/radio_control_value_accessor.ts index eb822c6b86..a62396789d 100644 --- a/modules/@angular/common/src/forms/directives/radio_control_value_accessor.ts +++ b/modules/@angular/forms/src/directives/radio_control_value_accessor.ts @@ -1,7 +1,7 @@ import {Directive, ElementRef, Injectable, Injector, Input, OnDestroy, OnInit, Renderer, forwardRef} from '@angular/core'; -import {ListWrapper} from '../../facade/collection'; -import {isPresent} from '../../facade/lang'; +import {ListWrapper} from '../facade/collection'; +import {isPresent} from '../facade/lang'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; import {NgControl} from './ng_control'; diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts similarity index 96% rename from modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts rename to modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts index 0802c036dd..c69fcd09e4 100644 --- a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_directive.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_control_directive.ts @@ -1,7 +1,7 @@ import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; -import {EventEmitter, ObservableWrapper} from '../../../facade/async'; -import {StringMapWrapper} from '../../../facade/collection'; +import {EventEmitter, ObservableWrapper} from '../../facade/async'; +import {StringMapWrapper} from '../../facade/collection'; import {FormControl} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts b/modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts similarity index 98% rename from modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts rename to modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts index 2c17763020..45c8e89b20 100644 --- a/modules/@angular/common/src/forms/directives/reactive_directives/form_control_name.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_control_name.ts @@ -1,6 +1,6 @@ import {Directive, Host, Inject, Input, OnChanges, OnDestroy, Optional, Output, Self, SimpleChanges, SkipSelf, forwardRef} from '@angular/core'; -import {EventEmitter, ObservableWrapper} from '../../../facade/async'; +import {EventEmitter, ObservableWrapper} from '../../facade/async'; import {FormControl} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators'; diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts similarity index 95% rename from modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts rename to modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts index 1f3278efe7..f22e129e75 100644 --- a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_directive.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts @@ -1,9 +1,9 @@ import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; -import {EventEmitter, ObservableWrapper} from '../../../facade/async'; -import {ListWrapper, StringMapWrapper} from '../../../facade/collection'; -import {BaseException} from '../../../facade/exceptions'; -import {isBlank} from '../../../facade/lang'; +import {EventEmitter, ObservableWrapper} from '../../facade/async'; +import {ListWrapper, StringMapWrapper} from '../../facade/collection'; +import {BaseException} from '../../facade/exceptions'; +import {isBlank} from '../../facade/lang'; import {FormControl, FormGroup} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; import {ControlContainer} from '../control_container'; diff --git a/modules/@angular/common/src/forms/directives/reactive_directives/form_group_name.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_name.ts similarity index 100% rename from modules/@angular/common/src/forms/directives/reactive_directives/form_group_name.ts rename to modules/@angular/forms/src/directives/reactive_directives/form_group_name.ts diff --git a/modules/@angular/common/src/forms/directives/select_control_value_accessor.ts b/modules/@angular/forms/src/directives/select_control_value_accessor.ts similarity index 97% rename from modules/@angular/common/src/forms/directives/select_control_value_accessor.ts rename to modules/@angular/forms/src/directives/select_control_value_accessor.ts index 0ff036f5c9..ff03603979 100644 --- a/modules/@angular/common/src/forms/directives/select_control_value_accessor.ts +++ b/modules/@angular/forms/src/directives/select_control_value_accessor.ts @@ -1,7 +1,7 @@ import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core'; -import {MapWrapper} from '../../facade/collection'; -import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../../facade/lang'; +import {MapWrapper} from '../facade/collection'; +import {StringWrapper, isBlank, isPresent, isPrimitive, looseIdentical} from '../facade/lang'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; diff --git a/modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts b/modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts similarity index 98% rename from modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts rename to modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts index 860f7e7e13..886f73e445 100644 --- a/modules/@angular/common/src/forms/directives/select_multiple_control_value_accessor.ts +++ b/modules/@angular/forms/src/directives/select_multiple_control_value_accessor.ts @@ -1,7 +1,7 @@ import {Directive, ElementRef, Host, Input, OnDestroy, Optional, Renderer, forwardRef} from '@angular/core'; -import {MapWrapper} from '../../facade/collection'; -import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../../facade/lang'; +import {MapWrapper} from '../facade/collection'; +import {StringWrapper, isBlank, isPresent, isPrimitive, isString, looseIdentical} from '../facade/lang'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from './control_value_accessor'; diff --git a/modules/@angular/common/src/forms/directives/shared.ts b/modules/@angular/forms/src/directives/shared.ts similarity index 96% rename from modules/@angular/common/src/forms/directives/shared.ts rename to modules/@angular/forms/src/directives/shared.ts index 827595fc5d..55dd9c54f0 100644 --- a/modules/@angular/common/src/forms/directives/shared.ts +++ b/modules/@angular/forms/src/directives/shared.ts @@ -1,6 +1,6 @@ -import {ListWrapper, StringMapWrapper} from '../../facade/collection'; -import {BaseException} from '../../facade/exceptions'; -import {hasConstructor, isBlank, isPresent, looseIdentical} from '../../facade/lang'; +import {ListWrapper, StringMapWrapper} from '../facade/collection'; +import {BaseException} from '../facade/exceptions'; +import {hasConstructor, isBlank, isPresent, looseIdentical} from '../facade/lang'; import {FormControl, FormGroup} from '../model'; import {Validators} from '../validators'; diff --git a/modules/@angular/common/src/forms/directives/validators.ts b/modules/@angular/forms/src/directives/validators.ts similarity index 98% rename from modules/@angular/common/src/forms/directives/validators.ts rename to modules/@angular/forms/src/directives/validators.ts index fff44651bf..3c45ece10c 100644 --- a/modules/@angular/common/src/forms/directives/validators.ts +++ b/modules/@angular/forms/src/directives/validators.ts @@ -1,6 +1,6 @@ import {Attribute, Directive, forwardRef} from '@angular/core'; -import {NumberWrapper} from '../../facade/lang'; +import {NumberWrapper} from '../facade/lang'; import {AbstractControl} from '../model'; import {NG_VALIDATORS, Validators} from '../validators'; diff --git a/modules/@angular/forms/src/facade/async.dart b/modules/@angular/forms/src/facade/async.dart new file mode 100644 index 0000000000..899a411b99 --- /dev/null +++ b/modules/@angular/forms/src/facade/async.dart @@ -0,0 +1,132 @@ +library angular2.core.facade.async; + +import 'dart:async'; +export 'dart:async' show Stream, StreamController, StreamSubscription; + +export 'promise.dart'; + +class TimerWrapper { + static Timer setTimeout(fn(), int millis) => + new Timer(new Duration(milliseconds: millis), fn); + static void clearTimeout(Timer timer) { + timer.cancel(); + } + + static Timer setInterval(fn(), int millis) { + var interval = new Duration(milliseconds: millis); + return new Timer.periodic(interval, (Timer timer) { + fn(); + }); + } + + static void clearInterval(Timer timer) { + timer.cancel(); + } +} + +class ObservableWrapper { + static StreamSubscription subscribe/**/(Stream s, onNext(/*=T*/ value), + [onError, onComplete]) { + return s.listen(onNext, + onError: onError, onDone: onComplete, cancelOnError: true); + } + + static bool isObservable(obs) { + return obs is Stream; + } + + /** + * Returns whether `emitter` has any subscribers listening to events. + */ + static bool hasSubscribers(EventEmitter emitter) { + return emitter._controller.hasListener; + } + + static void dispose(StreamSubscription s) { + s.cancel(); + } + + @Deprecated('Use callEmit() instead') + static void callNext(EventEmitter emitter, value) { + emitter.add(value); + } + + static void callEmit(EventEmitter emitter, value) { + emitter.add(value); + } + + static void callError(EventEmitter emitter, error) { + emitter.addError(error); + } + + static void callComplete(EventEmitter emitter) { + emitter.close(); + } + + static Stream fromPromise(Future f) { + return new Stream.fromFuture(f); + } + + static Future toPromise(Stream s) { + return s.single; + } +} + +class EventEmitter extends Stream { + StreamController _controller; + + /// Creates an instance of [EventEmitter], which depending on [isAsync], + /// delivers events synchronously or asynchronously. + EventEmitter([bool isAsync = true]) { + _controller = new StreamController.broadcast(sync: !isAsync); + } + + StreamSubscription listen(void onData(T event), + {Function onError, void onDone(), bool cancelOnError}) { + return _controller.stream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); + } + + void add(value) { + _controller.add(value); + } + + void emit(value) { + _controller.add(value); + } + + void addError(error) { + _controller.addError(error); + } + + void close() { + _controller.close(); + } +} + +//todo(robwormald): maybe fix in ts2dart? +class Subject extends Stream { + StreamController _controller; + + Subject([bool isAsync = true]) { + _controller = new StreamController.broadcast(sync: !isAsync); + } + + StreamSubscription listen(void onData(T data), + {Function onError, void onDone(), bool cancelOnError}) { + return _controller.stream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); + } + + void add(value) { + _controller.add(value); + } + + void addError(error) { + _controller.addError(error); + } + + void close() { + _controller.close(); + } +} diff --git a/modules/@angular/forms/src/facade/async.ts b/modules/@angular/forms/src/facade/async.ts new file mode 100644 index 0000000000..d9433792e3 --- /dev/null +++ b/modules/@angular/forms/src/facade/async.ts @@ -0,0 +1,169 @@ +import {Observable} from 'rxjs/Observable'; +import {Subject} from 'rxjs/Subject'; +import {PromiseObservable} from 'rxjs/observable/PromiseObservable'; +import {toPromise} from 'rxjs/operator/toPromise'; + +import {global, noop} from './lang'; + +export {Observable} from 'rxjs/Observable'; +export {Subject} from 'rxjs/Subject'; +export {PromiseCompleter, PromiseWrapper} from './promise'; + +export class TimerWrapper { + static setTimeout(fn: (...args: any[]) => void, millis: number): number { + return global.setTimeout(fn, millis); + } + static clearTimeout(id: number): void { global.clearTimeout(id); } + + static setInterval(fn: (...args: any[]) => void, millis: number): number { + return global.setInterval(fn, millis); + } + static clearInterval(id: number): void { global.clearInterval(id); } +} + +export class ObservableWrapper { + // TODO(vsavkin): when we use rxnext, try inferring the generic type from the first arg + static subscribe( + emitter: any, onNext: (value: T) => void, onError?: (exception: any) => void, + onComplete: () => void = () => {}): Object { + onError = (typeof onError === 'function') && onError || noop; + onComplete = (typeof onComplete === 'function') && onComplete || noop; + return emitter.subscribe({next: onNext, error: onError, complete: onComplete}); + } + + static isObservable(obs: any): boolean { return !!obs.subscribe; } + + /** + * Returns whether `obs` has any subscribers listening to events. + */ + static hasSubscribers(obs: EventEmitter): boolean { return obs.observers.length > 0; } + + static dispose(subscription: any) { subscription.unsubscribe(); } + + /** + * @deprecated - use callEmit() instead + */ + static callNext(emitter: EventEmitter, value: any) { emitter.next(value); } + + static callEmit(emitter: EventEmitter, value: any) { emitter.emit(value); } + + static callError(emitter: EventEmitter, error: any) { emitter.error(error); } + + static callComplete(emitter: EventEmitter) { emitter.complete(); } + + static fromPromise(promise: Promise): Observable { + return PromiseObservable.create(promise); + } + + static toPromise(obj: Observable): Promise { return toPromise.call(obj); } +} + +/** + * Use by directives and components to emit custom Events. + * + * ### Examples + * + * In the following example, `Zippy` alternatively emits `open` and `close` events when its + * title gets clicked: + * + * ``` + * @Component({ + * selector: 'zippy', + * template: ` + *
+ *
Toggle
+ *
+ * + *
+ *
`}) + * export class Zippy { + * visible: boolean = true; + * @Output() open: EventEmitter = new EventEmitter(); + * @Output() close: EventEmitter = new EventEmitter(); + * + * toggle() { + * this.visible = !this.visible; + * if (this.visible) { + * this.open.emit(null); + * } else { + * this.close.emit(null); + * } + * } + * } + * ``` + * + * The events payload can be accessed by the parameter `$event` on the components output event + * handler: + * + * ``` + * + * ``` + * + * Uses Rx.Observable but provides an adapter to make it work as specified here: + * https://github.com/jhusain/observable-spec + * + * Once a reference implementation of the spec is available, switch to it. + * @stable + */ +export class EventEmitter extends Subject { + // TODO: mark this as internal once all the facades are gone + // we can't mark it as internal now because EventEmitter exported via @angular/core would not + // contain this property making it incompatible with all the code that uses EventEmitter via + // facades, which are local to the code and do not have this property stripped. + // tslint:disable-next-line + __isAsync: boolean; + + /** + * Creates an instance of [EventEmitter], which depending on [isAsync], + * delivers events synchronously or asynchronously. + */ + constructor(isAsync: boolean = false) { + super(); + this.__isAsync = isAsync; + } + + emit(value: T) { super.next(value); } + + /** + * @deprecated - use .emit(value) instead + */ + next(value: any) { super.next(value); } + + subscribe(generatorOrNext?: any, error?: any, complete?: any): any { + let schedulerFn: any /** TODO #9100 */; + let errorFn = (err: any): any /** TODO #9100 */ => null; + let completeFn = (): any /** TODO #9100 */ => null; + + if (generatorOrNext && typeof generatorOrNext === 'object') { + schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => { + setTimeout(() => generatorOrNext.next(value)); + } : (value: any /** TODO #9100 */) => { generatorOrNext.next(value); }; + + if (generatorOrNext.error) { + errorFn = this.__isAsync ? (err) => { setTimeout(() => generatorOrNext.error(err)); } : + (err) => { generatorOrNext.error(err); }; + } + + if (generatorOrNext.complete) { + completeFn = this.__isAsync ? () => { setTimeout(() => generatorOrNext.complete()); } : + () => { generatorOrNext.complete(); }; + } + } else { + schedulerFn = this.__isAsync ? (value: any /** TODO #9100 */) => { + setTimeout(() => generatorOrNext(value)); + } : (value: any /** TODO #9100 */) => { generatorOrNext(value); }; + + if (error) { + errorFn = + this.__isAsync ? (err) => { setTimeout(() => error(err)); } : (err) => { error(err); }; + } + + if (complete) { + completeFn = + this.__isAsync ? () => { setTimeout(() => complete()); } : () => { complete(); }; + } + } + + return super.subscribe(schedulerFn, errorFn, completeFn); + } +} diff --git a/modules/@angular/forms/src/facade/base_wrapped_exception.dart b/modules/@angular/forms/src/facade/base_wrapped_exception.dart new file mode 100644 index 0000000000..ab3b913f55 --- /dev/null +++ b/modules/@angular/forms/src/facade/base_wrapped_exception.dart @@ -0,0 +1,17 @@ +library angular.core.facade.base_wrapped_exception; + +/** + * A base class for the WrappedException that can be used to identify + * a WrappedException from ExceptionHandler without adding circular + * dependency. + */ +class BaseWrappedException extends Error { + BaseWrappedException(); + + get originalException => null; + get originalStack => null; + + String get message => ''; + String get wrapperMessage => ''; + dynamic get context => null; +} diff --git a/modules/@angular/forms/src/facade/base_wrapped_exception.ts b/modules/@angular/forms/src/facade/base_wrapped_exception.ts new file mode 100644 index 0000000000..22b486778e --- /dev/null +++ b/modules/@angular/forms/src/facade/base_wrapped_exception.ts @@ -0,0 +1,15 @@ +/** + * A base class for the WrappedException that can be used to identify + * a WrappedException from ExceptionHandler without adding circular + * dependency. + */ +export class BaseWrappedException extends Error { + constructor(message: string) { super(message); } + + get wrapperMessage(): string { return ''; } + get wrapperStack(): any { return null; } + get originalException(): any { return null; } + get originalStack(): any { return null; } + get context(): any { return null; } + get message(): string { return ''; } +} diff --git a/modules/@angular/forms/src/facade/browser.dart b/modules/@angular/forms/src/facade/browser.dart new file mode 100644 index 0000000000..8c364d7f7d --- /dev/null +++ b/modules/@angular/forms/src/facade/browser.dart @@ -0,0 +1,32 @@ +/** + * Dart version of browser APIs. This library depends on 'dart:html' and + * therefore can only run in the browser. + */ +library angular2.src.facade.browser; + +import 'dart:js' show context; +import 'dart:html' show Location, window; + +export 'dart:html' + show + document, + window, + Element, + Node, + MouseEvent, + KeyboardEvent, + Event, + EventTarget, + History, + Location, + EventListener; + +Location get location => window.location; + +final _gc = context['gc']; + +void gc() { + if (_gc != null) { + _gc.apply(const []); + } +} diff --git a/modules/@angular/forms/src/facade/browser.ts b/modules/@angular/forms/src/facade/browser.ts new file mode 100644 index 0000000000..ca8277f540 --- /dev/null +++ b/modules/@angular/forms/src/facade/browser.ts @@ -0,0 +1,17 @@ +/** + * JS version of browser APIs. This library can only run in the browser. + */ +var win = typeof window !== 'undefined' && window || {}; + +export {win as window}; +export var document = win.document; +export var location = win.location; +export var gc = win['gc'] ? () => win['gc']() : (): any /** TODO #9100 */ => null; +export var performance = win['performance'] ? win['performance'] : null; +export const Event = win['Event']; +export const MouseEvent = win['MouseEvent']; +export const KeyboardEvent = win['KeyboardEvent']; +export const EventTarget = win['EventTarget']; +export const History = win['History']; +export const Location = win['Location']; +export const EventListener = win['EventListener']; diff --git a/modules/@angular/forms/src/facade/collection.dart b/modules/@angular/forms/src/facade/collection.dart new file mode 100644 index 0000000000..cabedff0a4 --- /dev/null +++ b/modules/@angular/forms/src/facade/collection.dart @@ -0,0 +1,287 @@ +library facade.collection; + +import 'dart:collection' show IterableBase; +import 'dart:convert' show JsonEncoder; +export 'dart:core' show Iterator, Map, List, Set; +import 'dart:math' show max, min; + +var jsonEncoder = new JsonEncoder(); + +class MapIterator extends Iterator { + final Iterator _iterator; + final Map _map; + + MapIterator(Map map) + : _map = map, + _iterator = map.keys.iterator; + + bool moveNext() => _iterator.moveNext(); + + List get current { + return _iterator.current != null + ? [_iterator.current, _map[_iterator.current]] + : null; + } +} + +class IterableMap extends IterableBase { + final Map _map; + + IterableMap(Map map) : _map = map; + + Iterator get iterator => new MapIterator(_map); +} + +class MapWrapper { + static Map/**/ clone/**/(Map/**/ m) => new Map.from(m); + + // in opposite to JS, Dart does not create a new map + static Map/**/ createFromStringMap/**/(Map/**/ m) => m; + + // in opposite to JS, Dart does not create a new map + static Map/**/ toStringMap/**/(Map/**/ m) => m; + + static Map/**/ createFromPairs/**/(List pairs) => pairs.fold(/**/{}, (m, p) { + m[p[0]] = p[1]; + return m; + }); + + static void clearValues(Map m) { + for (var k in m.keys) { + m[k] = null; + } + } + + static Iterable/*>*/ iterable/**/(Map/**/ m) => new IterableMap(m); + static List/**/ keys/**/(Map/**/ m) => m.keys.toList(); + static List/**/ values/**/(Map/**/ m) => m.values.toList(); +} + +class StringMapWrapper { + static Map/**/ create/**/() => {}; + static bool contains/**/(Map/**/ map, String key) => map.containsKey(key); + static get/**/(Map/**/ map, String key) => map[key]; + static void set/**/(Map/**/ map, String key, /*=V*/value) { + map[key] = value; + } + + static void delete/**/(Map/**/ m, String k) { + m.remove(k); + } + + static void forEach/**/(Map/**/ m, fn(/*=V*/ v, String k)) { + m.forEach((k, v) => fn(v, k)); + } + + static Map/**/ merge/**/(Map/**/ a, Map/**/ b) { + var m = new Map/**/.from(a); + if (b != null) { + b.forEach((k, v) => m[k] = v); + } + return m; + } + + static List keys(Map a) { + return a.keys.toList(); + } + + static List values(Map a) { + return a.values.toList(); + } + + static bool isEmpty(Map m) => m.isEmpty; + static bool equals/**/(Map/**/ m1, Map/**/ m2) { + if (m1.length != m2.length) { + return false; + } + for (var key in m1.keys) { + if (m1[key] != m2[key]) { + return false; + } + } + return true; + } +} + +typedef bool Predicate(T item); + +class ListWrapper { + static List/**/ clone/**/(Iterable/**/ l) => new List.from(l); + static List/**/ createFixedSize/**/(int size) => new List(size); + static List/**/ createGrowableSize/**/(int size) => + new List.generate(size, (_) => null, growable: true); + + static bool contains(List m, k) => m.contains(k); + static int indexOf(List list, value, [int startIndex = 0]) => + list.indexOf(value, startIndex); + static int lastIndexOf(List list, value, [int startIndex = null]) => + list.lastIndexOf(value, startIndex == null ? list.length : startIndex); + + static void forEachWithIndex/**/(List/**/ list, fn(/*=T*/ item, int index)) { + for (var i = 0; i < list.length; ++i) { + fn(list[i], i); + } + } + static /*=T*/ first/**/(List/**/ list) => list.isEmpty ? null : list.first; + static /*=T*/ last/**/(List/**/ list) => list.isEmpty ? null : list.last; + static List/**/ reversed/**/(List/**/ list) => list.reversed.toList(); + static List/**/ concat/**/(List/**/ a, List/**/ b) { + return new List() + ..length = a.length + b.length + ..setRange(0, a.length, a) + ..setRange(a.length, a.length + b.length, b); + } + + static void insert/**/(List/**/ l, int index, /*=T*/ value) { + l.insert(index, value); + } + + static removeAt(List l, int index) => l.removeAt(index); + static void removeAll/**/(List/**/ list, List/**/ items) { + for (var i = 0; i < items.length; ++i) { + list.remove(items[i]); + } + } + + static bool remove/**/(List/**/ list, /*=T*/ item) => list.remove(item); + static void clear(List l) { + l.clear(); + } + + static bool isEmpty(Iterable list) => list.isEmpty; + static void fill/**/(List/**/ l, /*=T*/ value, [int start = 0, int end]) { + l.fillRange(_startOffset(l, start), _endOffset(l, end), value); + } + + static bool equals/**/(List/**/ a, List/**/ b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; ++i) { + if (a[i] != b[i]) return false; + } + return true; + } + + static List/**/ slice/**/(List/**/ l, [int from = 0, int to]) { + from = _startOffset(l, from); + to = _endOffset(l, to); + //in JS if from > to an empty array is returned + if (to != null && from > to) { + return []; + } + return l.sublist(from, to); + } + + static List/**/ splice/**/(List/**/ l, int from, int length) { + from = _startOffset(l, from); + var to = from + length; + var sub = l.sublist(from, to); + l.removeRange(from, to); + return sub; + } + + static void sort/**/(List/**/ l, [int compareFn(/*=T*/a, /*=T*/b) = null]) { + if (compareFn == null) { + l.sort(); + } else { + l.sort(compareFn); + } + } + + static String toJSON(List l) { + return jsonEncoder.convert(l); + } + + // JS splice, slice, fill functions can take start < 0 which indicates a position relative to + // the end of the list + static int _startOffset(List l, int start) { + int len = l.length; + return start < 0 ? max(len + start, 0) : min(start, len); + } + + // JS splice, slice, fill functions can take end < 0 which indicates a position relative to + // the end of the list + static int _endOffset(List l, int end) { + int len = l.length; + if (end == null) return len; + return end < 0 ? max(len + end, 0) : min(end, len); + } + + static maximum(List l, fn(item)) { + if (l.length == 0) { + return null; + } + var solution = null; + var maxValue = double.NEGATIVE_INFINITY; + for (var index = 0; index < l.length; index++) { + var candidate = l[index]; + if (candidate == null) { + continue; + } + var candidateValue = fn(candidate); + if (candidateValue > maxValue) { + solution = candidate; + maxValue = candidateValue; + } + } + return solution; + } + + static List flatten(List l) { + var target = []; + _flattenArray(l, target); + return target; + } + + static addAll(List l, List source) { + l.addAll(source); + } +} + +List _flattenArray(List source, List target) { + if (source != null) { + for (var i = 0; i < source.length; i++) { + var item = source[i]; + if (item is List) { + _flattenArray(item, target); + } else { + target.add(item); + } + } + } + return target; +} + + +bool isListLikeIterable(obj) => obj is Iterable; + +bool areIterablesEqual/**/( + Iterable/**/ a, + Iterable/**/ b, + bool comparator(/*=T*/a, /*=T*/b)) +{ + var iterator1 = a.iterator; + var iterator2 = b.iterator; + + while (true) { + var done1 = !iterator1.moveNext(); + var done2 = !iterator2.moveNext(); + if (done1 && done2) return true; + if (done1 || done2) return false; + if (!comparator(iterator1.current, iterator2.current)) return false; + } +} + +void iterateListLike/**/(Iterable/**/ iter, fn(/*=T*/item)) { + assert(iter is Iterable); + for (var item in iter) { + fn(item); + } +} + +class SetWrapper { + static Set/**/ createFromList/**/(List/**/ l) => new Set.from(l); + static bool has/**/(Set/**/ s, /*=T*/key) => s.contains(key); + static void delete/**/(Set/**/ m, /*=T*/k) { + m.remove(k); + } +} diff --git a/modules/@angular/forms/src/facade/collection.ts b/modules/@angular/forms/src/facade/collection.ts new file mode 100644 index 0000000000..cf67e2510d --- /dev/null +++ b/modules/@angular/forms/src/facade/collection.ts @@ -0,0 +1,352 @@ +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() { + try { + if (new Map([[1, 2]]).size === 1) { + return function createMapFromPairs(pairs: any[]): Map { return new Map(pairs); }; + } + } catch (e) { + } + return function createMapAndPopulateFromPairs(pairs: any[]): Map { + var map = new Map(); + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i]; + map.set(pair[0], pair[1]); + } + return map; + }; +})(); +var createMapFromMap: {(m: Map): Map} = (function() { + try { + if (new Map(new Map())) { + return function createMapFromMap(m: Map): Map { return new Map(m); }; + } + } catch (e) { + } + return function createMapAndPopulateFromMap(m: Map): Map { + var map = new Map(); + m.forEach((v, k) => { map.set(k, v); }); + return map; + }; +})(); +var _clearValues: {(m: Map): void} = (function() { + if (((new Map()).keys()).next) { + return function _clearValues(m: Map) { + var keyIterator = m.keys(); + var k: any /** TODO #???? */; + while (!((k = (keyIterator).next()).done)) { + m.set(k.value, null); + } + }; + } else { + return function _clearValuesWithForeEach(m: Map) { + m.forEach((v, k) => { m.set(k, null); }); + }; + } +})(); +// Safari doesn't implement MapIterator.next(), which is used is Traceur's polyfill of Array.from +// TODO(mlaval): remove the work around once we have a working polyfill of Array.from +var _arrayFromMap: {(m: Map, getValues: boolean): any[]} = (function() { + try { + if (((new Map()).values()).next) { + return function createArrayFromMap(m: Map, getValues: boolean): any[] { + return getValues ? (Array).from(m.values()) : (Array).from(m.keys()); + }; + } + } catch (e) { + } + return function createArrayFromMapWithForeach(m: Map, getValues: boolean): any[] { + var res = ListWrapper.createFixedSize(m.size), i = 0; + m.forEach((v, k) => { + res[i] = getValues ? v : k; + i++; + }); + return res; + }; +})(); + +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) { + result.set(prop, stringMap[prop]); + } + return result; + } + static toStringMap(m: Map): {[key: string]: T} { + var r: {[key: string]: T} = {}; + m.forEach((v, k) => r[k] = v); + 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); } +} + +/** + * 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; + } + static set(map: {[key: string]: V}, key: string, value: V) { map[key] = value; } + static keys(map: {[key: string]: any}): string[] { return Object.keys(map); } + static values(map: {[key: string]: T}): T[] { + return Object.keys(map).reduce((r, a) => { + r.push(map[a]); + return r; + }, []); + } + static isEmpty(map: {[key: string]: any}): boolean { + for (var prop in map) { + return false; + } + return true; + } + static delete (map: {[key: string]: any}, key: string) { delete map[key]; } + static forEach(map: {[key: string]: V}, callback: /*(V, K) => void*/ Function) { + for (var prop in map) { + if (map.hasOwnProperty(prop)) { + callback(map[prop], prop); + } + } + } + + static merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { + var m: {[key: string]: V} = {}; + + for (var attr in m1) { + if (m1.hasOwnProperty(attr)) { + m[attr] = m1[attr]; + } + } + + for (var attr in m2) { + if (m2.hasOwnProperty(attr)) { + m[attr] = m2[attr]; + } + } + + return m; + } + + static equals(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean { + var k1 = Object.keys(m1); + var k2 = Object.keys(m2); + if (k1.length != k2.length) { + return false; + } + var key: any /** TODO #???? */; + for (var i = 0; i < k1.length; i++) { + key = k1[i]; + if (m1[key] !== m2[key]) { + return false; + } + } + return true; + } +} + +/** + * A boolean-valued function over a value, possibly including context information + * regarding that value's position in an array. + */ +export interface Predicate { (value: T, index?: number, array?: T[]): boolean; } + +export class ListWrapper { + // JS has no way to express a statically fixed size list, but dart does so we + // keep both methods. + static createFixedSize(size: number): any[] { return new Array(size); } + static createGrowableSize(size: number): any[] { return new Array(size); } + static clone(array: T[]): T[] { return array.slice(0); } + static forEachWithIndex(array: T[], fn: (t: T, n: number) => void) { + for (var i = 0; i < array.length; i++) { + fn(array[i], i); + } + } + static first(array: T[]): T { + if (!array) return null; + return array[0]; + } + static last(array: T[]): T { + if (!array || array.length == 0) return null; + return array[array.length - 1]; + } + static indexOf(array: T[], value: T, startIndex: number = 0): number { + return array.indexOf(value, startIndex); + } + static contains(list: T[], el: T): boolean { return list.indexOf(el) !== -1; } + static reversed(array: T[]): T[] { + var a = ListWrapper.clone(array); + return a.reverse(); + } + static concat(a: any[], b: any[]): any[] { return a.concat(b); } + static insert(list: T[], index: number, value: T) { list.splice(index, 0, value); } + static removeAt(list: T[], index: number): T { + var res = list[index]; + list.splice(index, 1); + return res; + } + static removeAll(list: T[], items: T[]) { + for (var i = 0; i < items.length; ++i) { + var index = list.indexOf(items[i]); + list.splice(index, 1); + } + } + static remove(list: T[], el: T): boolean { + var index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + return true; + } + return false; + } + static clear(list: any[]) { list.length = 0; } + static isEmpty(list: any[]): boolean { return list.length == 0; } + static fill(list: any[], value: any, start: number = 0, end: number = null) { + list.fill(value, start, end === null ? list.length : end); + } + static equals(a: any[], b: any[]): boolean { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + static slice(l: T[], from: number = 0, to: number = null): T[] { + return l.slice(from, to === null ? undefined : to); + } + static splice(l: T[], from: number, length: number): T[] { return l.splice(from, length); } + static sort(l: T[], compareFn?: (a: T, b: T) => number) { + if (isPresent(compareFn)) { + l.sort(compareFn); + } else { + l.sort(); + } + } + static toString(l: T[]): string { return l.toString(); } + static toJSON(l: T[]): string { return JSON.stringify(l); } + + static maximum(list: T[], predicate: (t: T) => number): T { + if (list.length == 0) { + return null; + } + var solution: any /** TODO #???? */ = null; + var maxValue = -Infinity; + for (var index = 0; index < list.length; index++) { + var candidate = list[index]; + if (isBlank(candidate)) { + continue; + } + var candidateValue = predicate(candidate); + if (candidateValue > maxValue) { + solution = candidate; + maxValue = candidateValue; + } + } + return solution; + } + + static flatten(list: Array): T[] { + var target: any[] /** TODO #???? */ = []; + _flattenArray(list, target); + return target; + } + + static addAll(list: Array, source: Array): void { + for (var i = 0; i < source.length; i++) { + list.push(source[i]); + } + } +} + +function _flattenArray(source: any[], target: any[]): any[] { + if (isPresent(source)) { + for (var i = 0; i < source.length; i++) { + var item = source[i]; + if (isArray(item)) { + _flattenArray(item, target); + } else { + target.push(item); + } + } + } + return target; +} + + +export function isListLikeIterable(obj: any): boolean { + if (!isJsObject(obj)) return false; + return isArray(obj) || + (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v] + getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop +} + +export function areIterablesEqual(a: any, b: any, comparator: Function): boolean { + var iterator1 = a[getSymbolIterator()](); + var iterator2 = b[getSymbolIterator()](); + + while (true) { + let item1 = iterator1.next(); + let item2 = iterator2.next(); + if (item1.done && item2.done) return true; + if (item1.done || item2.done) return false; + if (!comparator(item1.value, item2.value)) return false; + } +} + +export function iterateListLike(obj: any, fn: Function) { + if (isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + fn(obj[i]); + } + } else { + var iterator = obj[getSymbolIterator()](); + var item: any /** TODO #???? */; + while (!((item = iterator.next()).done)) { + fn(item.value); + } + } +} + +// Safari and Internet Explorer do not support the iterable parameter to the +// Set constructor. We work around that by manually adding the items. +var createSetFromList: {(lst: any[]): Set} = (function() { + var test = new Set([1, 2, 3]); + if (test.size === 3) { + return function createSetFromList(lst: any[]): Set { return new Set(lst); }; + } else { + return function createSetAndPopulateFromList(lst: any[]): Set { + var res = new Set(lst); + if (res.size !== lst.length) { + for (var i = 0; i < lst.length; i++) { + res.add(lst[i]); + } + } + return res; + }; + } +})(); +export class SetWrapper { + static createFromList(lst: T[]): Set { return createSetFromList(lst); } + static has(s: Set, key: T): boolean { return s.has(key); } + static delete(m: Set, k: K) { m.delete(k); } +} diff --git a/modules/@angular/forms/src/facade/exception_handler.ts b/modules/@angular/forms/src/facade/exception_handler.ts new file mode 100644 index 0000000000..86d2bf4e53 --- /dev/null +++ b/modules/@angular/forms/src/facade/exception_handler.ts @@ -0,0 +1,133 @@ +import {BaseWrappedException} from './base_wrapped_exception'; +import {isListLikeIterable} from './collection'; +import {isBlank, isPresent} from './lang'; + +class _ArrayLogger { + res: any[] = []; + log(s: any): void { this.res.push(s); } + logError(s: any): void { this.res.push(s); } + logGroup(s: any): void { this.res.push(s); } + logGroupEnd(){}; +} + +/** + * Provides a hook for centralized exception handling. + * + * The default implementation of `ExceptionHandler` prints error messages to the `Console`. To + * intercept error handling, + * write a custom exception handler that replaces this default as appropriate for your app. + * + * ### Example + * + * ```javascript + * + * class MyExceptionHandler implements ExceptionHandler { + * call(error, stackTrace = null, reason = null) { + * // do something with the exception + * } + * } + * + * bootstrap(MyApp, {provide: ExceptionHandler, useClass: MyExceptionHandler}]) + * + * ``` + * @stable + */ +export class ExceptionHandler { + constructor(private _logger: any, private _rethrowException: boolean = true) {} + + static exceptionToString(exception: any, stackTrace: any = null, reason: string = null): string { + var l = new _ArrayLogger(); + var e = new ExceptionHandler(l, false); + e.call(exception, stackTrace, reason); + return l.res.join('\n'); + } + + call(exception: any, stackTrace: any = null, reason: string = null): void { + var originalException = this._findOriginalException(exception); + var originalStack = this._findOriginalStack(exception); + var context = this._findContext(exception); + + this._logger.logGroup(`EXCEPTION: ${this._extractMessage(exception)}`); + + if (isPresent(stackTrace) && isBlank(originalStack)) { + this._logger.logError('STACKTRACE:'); + this._logger.logError(this._longStackTrace(stackTrace)); + } + + if (isPresent(reason)) { + this._logger.logError(`REASON: ${reason}`); + } + + if (isPresent(originalException)) { + this._logger.logError(`ORIGINAL EXCEPTION: ${this._extractMessage(originalException)}`); + } + + if (isPresent(originalStack)) { + this._logger.logError('ORIGINAL STACKTRACE:'); + this._logger.logError(this._longStackTrace(originalStack)); + } + + if (isPresent(context)) { + this._logger.logError('ERROR CONTEXT:'); + this._logger.logError(context); + } + + this._logger.logGroupEnd(); + + // We rethrow exceptions, so operations like 'bootstrap' will result in an error + // when an exception happens. If we do not rethrow, bootstrap will always succeed. + if (this._rethrowException) throw exception; + } + + /** @internal */ + _extractMessage(exception: any): string { + return exception instanceof BaseWrappedException ? exception.wrapperMessage : + exception.toString(); + } + + /** @internal */ + _longStackTrace(stackTrace: any): any { + return isListLikeIterable(stackTrace) ? (stackTrace).join('\n\n-----async gap-----\n') : + stackTrace.toString(); + } + + /** @internal */ + _findContext(exception: any): any { + try { + if (!(exception instanceof BaseWrappedException)) return null; + return isPresent(exception.context) ? exception.context : + this._findContext(exception.originalException); + } catch (e) { + // exception.context can throw an exception. if it happens, we ignore the context. + return null; + } + } + + /** @internal */ + _findOriginalException(exception: any): any { + if (!(exception instanceof BaseWrappedException)) return null; + + var e = exception.originalException; + while (e instanceof BaseWrappedException && isPresent(e.originalException)) { + e = e.originalException; + } + + return e; + } + + /** @internal */ + _findOriginalStack(exception: any): any { + if (!(exception instanceof BaseWrappedException)) return null; + + var e = exception; + var stack = exception.originalStack; + while (e instanceof BaseWrappedException && isPresent(e.originalException)) { + e = e.originalException; + if (e instanceof BaseWrappedException && isPresent(e.originalException)) { + stack = e.originalStack; + } + } + + return stack; + } +} diff --git a/modules/@angular/forms/src/facade/exceptions.dart b/modules/@angular/forms/src/facade/exceptions.dart new file mode 100644 index 0000000000..bc16f3ca22 --- /dev/null +++ b/modules/@angular/forms/src/facade/exceptions.dart @@ -0,0 +1,50 @@ +library angular.core.facade.exceptions; + +import 'base_wrapped_exception.dart'; +import 'exception_handler.dart'; +export 'exception_handler.dart'; + +class BaseException extends Error { + final String _message; + + BaseException([this._message]); + + String get message => _message; + + String toString() { + return this.message; + } +} + +class WrappedException extends BaseWrappedException { + final dynamic _context; + final String _wrapperMessage; + final originalException; + final originalStack; + + WrappedException( + [this._wrapperMessage, + this.originalException, + this.originalStack, + this._context]); + + String get message { + return ExceptionHandler.exceptionToString(this); + } + + String toString() { + return this.message; + } + + dynamic get context => _context; + + String get wrapperMessage => _wrapperMessage; +} + +Error makeTypeError([String message = ""]) { + return new BaseException(message); +} + +dynamic unimplemented() { + throw new BaseException('unimplemented'); +} diff --git a/modules/@angular/forms/src/facade/exceptions.ts b/modules/@angular/forms/src/facade/exceptions.ts new file mode 100644 index 0000000000..abd6c9c537 --- /dev/null +++ b/modules/@angular/forms/src/facade/exceptions.ts @@ -0,0 +1,56 @@ +import {BaseWrappedException} from './base_wrapped_exception'; +import {ExceptionHandler} from './exception_handler'; + +export {ExceptionHandler} from './exception_handler'; + +/** + * @stable + */ +export class BaseException extends Error { + public stack: any; + constructor(public message: string = '--') { + super(message); + this.stack = (new Error(message)).stack; + } + + toString(): string { return this.message; } +} + +/** + * Wraps an exception and provides additional context or information. + * @stable + */ +export class WrappedException extends BaseWrappedException { + private _wrapperStack: any; + + constructor( + private _wrapperMessage: string, private _originalException: any /** TODO #9100 */, + private _originalStack?: any /** TODO #9100 */, private _context?: any /** TODO #9100 */) { + super(_wrapperMessage); + this._wrapperStack = (new Error(_wrapperMessage)).stack; + } + + get wrapperMessage(): string { return this._wrapperMessage; } + + get wrapperStack(): any { return this._wrapperStack; } + + + get originalException(): any { return this._originalException; } + + get originalStack(): any { return this._originalStack; } + + + get context(): any { return this._context; } + + get message(): string { return ExceptionHandler.exceptionToString(this); } + + toString(): string { return this.message; } +} + +export function makeTypeError(message?: string): Error { + return new TypeError(message); +} + +export function unimplemented(): any { + throw new BaseException('unimplemented'); +} diff --git a/modules/@angular/forms/src/facade/intl.dart b/modules/@angular/forms/src/facade/intl.dart new file mode 100644 index 0000000000..c94b66be5b --- /dev/null +++ b/modules/@angular/forms/src/facade/intl.dart @@ -0,0 +1,57 @@ +library facade.intl; + +import 'package:intl/intl.dart'; + +String _normalizeLocale(String locale) => locale.replaceAll('-', '_'); + +enum NumberFormatStyle { Decimal, Percent, Currency } + +class NumberFormatter { + static String format(num number, String locale, NumberFormatStyle style, + {int minimumIntegerDigits: 1, + int minimumFractionDigits: 0, + int maximumFractionDigits: 3, + String currency, + bool currencyAsSymbol: false}) { + locale = _normalizeLocale(locale); + NumberFormat formatter; + switch (style) { + case NumberFormatStyle.Decimal: + formatter = new NumberFormat.decimalPattern(locale); + break; + case NumberFormatStyle.Percent: + formatter = new NumberFormat.percentPattern(locale); + break; + case NumberFormatStyle.Currency: + if (currencyAsSymbol) { + // See https://github.com/dart-lang/intl/issues/59. + throw new Exception( + 'Displaying currency as symbol is not supported.'); + } + formatter = new NumberFormat.currencyPattern(locale, currency); + break; + } + formatter.minimumIntegerDigits = minimumIntegerDigits; + formatter.minimumFractionDigits = minimumFractionDigits; + formatter.maximumFractionDigits = maximumFractionDigits; + return formatter.format(number); + } +} + +class DateFormatter { + static RegExp _multiPartRegExp = new RegExp(r'^([yMdE]+)([Hjms]+)$'); + + static String format(DateTime date, String locale, String pattern) { + locale = _normalizeLocale(locale); + var formatter = new DateFormat(null, locale); + var matches = _multiPartRegExp.firstMatch(pattern); + if (matches != null) { + // Support for patterns which have known date and time components. + formatter.addPattern(matches[1]); + formatter.addPattern(matches[2], ', '); + } else { + formatter.addPattern(pattern); + } + return formatter.format(date); + } +} diff --git a/modules/@angular/forms/src/facade/intl.ts b/modules/@angular/forms/src/facade/intl.ts new file mode 100644 index 0000000000..1a92f134e7 --- /dev/null +++ b/modules/@angular/forms/src/facade/intl.ts @@ -0,0 +1,207 @@ +export enum NumberFormatStyle { + Decimal, + Percent, + Currency +} + +export class NumberFormatter { + static format( + num: number, locale: string, style: NumberFormatStyle, + {minimumIntegerDigits = 1, minimumFractionDigits = 0, maximumFractionDigits = 3, currency, + currencyAsSymbol = false}: { + minimumIntegerDigits?: number, + minimumFractionDigits?: number, + maximumFractionDigits?: number, + currency?: string, + currencyAsSymbol?: boolean + } = {}): string { + var intlOptions: Intl.NumberFormatOptions = { + minimumIntegerDigits: minimumIntegerDigits, + minimumFractionDigits: minimumFractionDigits, + maximumFractionDigits: maximumFractionDigits + }; + intlOptions.style = NumberFormatStyle[style].toLowerCase(); + if (style == NumberFormatStyle.Currency) { + intlOptions.currency = currency; + intlOptions.currencyDisplay = currencyAsSymbol ? 'symbol' : 'code'; + } + return new Intl.NumberFormat(locale, intlOptions).format(num); + } +} +var DATE_FORMATS_SPLIT = + /((?:[^yMLdHhmsaZEwGjJ']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|J+|j+|m+|s+|a|Z|G+|w+))(.*)/; + +var PATTERN_ALIASES = { + yMMMdjms: datePartGetterFactory(combine([ + digitCondition('year', 1), + nameCondition('month', 3), + digitCondition('day', 1), + digitCondition('hour', 1), + digitCondition('minute', 1), + digitCondition('second', 1), + ])), + yMdjm: datePartGetterFactory(combine([ + digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1), + digitCondition('hour', 1), digitCondition('minute', 1) + ])), + yMMMMEEEEd: datePartGetterFactory(combine([ + digitCondition('year', 1), nameCondition('month', 4), nameCondition('weekday', 4), + digitCondition('day', 1) + ])), + yMMMMd: datePartGetterFactory( + combine([digitCondition('year', 1), nameCondition('month', 4), digitCondition('day', 1)])), + yMMMd: datePartGetterFactory( + combine([digitCondition('year', 1), nameCondition('month', 3), digitCondition('day', 1)])), + yMd: datePartGetterFactory( + combine([digitCondition('year', 1), digitCondition('month', 1), digitCondition('day', 1)])), + jms: datePartGetterFactory(combine( + [digitCondition('hour', 1), digitCondition('second', 1), digitCondition('minute', 1)])), + jm: datePartGetterFactory(combine([digitCondition('hour', 1), digitCondition('minute', 1)])) +}; + +var DATE_FORMATS = { + yyyy: datePartGetterFactory(digitCondition('year', 4)), + yy: datePartGetterFactory(digitCondition('year', 2)), + y: datePartGetterFactory(digitCondition('year', 1)), + MMMM: datePartGetterFactory(nameCondition('month', 4)), + MMM: datePartGetterFactory(nameCondition('month', 3)), + MM: datePartGetterFactory(digitCondition('month', 2)), + M: datePartGetterFactory(digitCondition('month', 1)), + LLLL: datePartGetterFactory(nameCondition('month', 4)), + dd: datePartGetterFactory(digitCondition('day', 2)), + d: datePartGetterFactory(digitCondition('day', 1)), + HH: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), false))), + H: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), false))), + hh: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 2), true))), + h: hourExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))), + jj: datePartGetterFactory(digitCondition('hour', 2)), + j: datePartGetterFactory(digitCondition('hour', 1)), + mm: datePartGetterFactory(digitCondition('minute', 2)), + m: datePartGetterFactory(digitCondition('minute', 1)), + ss: datePartGetterFactory(digitCondition('second', 2)), + s: datePartGetterFactory(digitCondition('second', 1)), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit + // fractions + sss: datePartGetterFactory(digitCondition('second', 3)), + EEEE: datePartGetterFactory(nameCondition('weekday', 4)), + EEE: datePartGetterFactory(nameCondition('weekday', 3)), + EE: datePartGetterFactory(nameCondition('weekday', 2)), + E: datePartGetterFactory(nameCondition('weekday', 1)), + a: hourClockExtracter(datePartGetterFactory(hour12Modify(digitCondition('hour', 1), true))), + Z: datePartGetterFactory({timeZoneName: 'long'}), + z: datePartGetterFactory({timeZoneName: 'short'}), + ww: datePartGetterFactory({}), // Week of year, padded (00-53). Week 01 is the week with the + // first Thursday of the year. not support ? + w: datePartGetterFactory({}), // Week of year (0-53). Week 1 is the week with the first Thursday + // of the year not support ? + G: datePartGetterFactory(nameCondition('era', 1)), + GG: datePartGetterFactory(nameCondition('era', 2)), + GGG: datePartGetterFactory(nameCondition('era', 3)), + GGGG: datePartGetterFactory(nameCondition('era', 4)) +}; + + +function hourClockExtracter(inner: (date: Date, locale: string) => string): ( + date: Date, locale: string) => string { + return function(date: Date, locale: string): string { + var result = inner(date, locale); + + return result.split(' ')[1]; + }; +} + +function hourExtracter(inner: (date: Date, locale: string) => string): ( + date: Date, locale: string) => string { + return function(date: Date, locale: string): string { + var result = inner(date, locale); + + return result.split(' ')[0]; + }; +} + +function hour12Modify( + options: Intl.DateTimeFormatOptions, value: boolean): Intl.DateTimeFormatOptions { + options.hour12 = value; + return options; +} + +function digitCondition(prop: string, len: number): Intl.DateTimeFormatOptions { + var result = {}; + (result as any /** TODO #9100 */)[prop] = len == 2 ? '2-digit' : 'numeric'; + return result; +} +function nameCondition(prop: string, len: number): Intl.DateTimeFormatOptions { + var result = {}; + (result as any /** TODO #9100 */)[prop] = len < 4 ? 'short' : 'long'; + return result; +} + +function combine(options: Intl.DateTimeFormatOptions[]): Intl.DateTimeFormatOptions { + var result = {}; + + options.forEach(option => { (Object).assign(result, option); }); + + return result; +} + +function datePartGetterFactory(ret: Intl.DateTimeFormatOptions): (date: Date, locale: string) => + string { + return function(date: Date, locale: string): string { + return new Intl.DateTimeFormat(locale, ret).format(date); + }; +} + + +var datePartsFormatterCache: Map = new Map(); + +function dateFormatter(format: string, date: Date, locale: string): string { + var text = ''; + var match: any /** TODO #9100 */; + var fn: any /** TODO #9100 */; + var parts: string[] = []; + if ((PATTERN_ALIASES as any /** TODO #9100 */)[format]) { + return (PATTERN_ALIASES as any /** TODO #9100 */)[format](date, locale); + } + + + if (datePartsFormatterCache.has(format)) { + parts = datePartsFormatterCache.get(format); + } else { + var matchs = DATE_FORMATS_SPLIT.exec(format); + + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + datePartsFormatterCache.set(format, parts); + } + + parts.forEach(part => { + fn = (DATE_FORMATS as any /** TODO #9100 */)[part]; + text += fn ? fn(date, locale) : + part === '\'\'' ? '\'' : part.replace(/(^'|'$)/g, '').replace(/''/g, '\''); + }); + + return text; +} + +var slice = [].slice; +function concat( + array1: any /** TODO #9100 */, array2: any /** TODO #9100 */, + index: any /** TODO #9100 */): string[] { + return array1.concat(slice.call(array2, index)); +} + +export class DateFormatter { + static format(date: Date, locale: string, pattern: string): string { + return dateFormatter(pattern, date, locale); + } +} diff --git a/modules/@angular/forms/src/facade/lang.dart b/modules/@angular/forms/src/facade/lang.dart new file mode 100644 index 0000000000..eb788121af --- /dev/null +++ b/modules/@angular/forms/src/facade/lang.dart @@ -0,0 +1,402 @@ +library angular.core.facade.lang; + +export 'dart:core' show Type, RegExp, print, DateTime, Uri; +import 'dart:math' as math; +import 'dart:convert' as convert; +import 'dart:async' show Future, Zone; + +String getTypeNameForDebugging(Object type) => type.toString(); + +class Math { + static final _random = new math.Random(); + static int floor(num n) => n.floor(); + static double random() => _random.nextDouble(); + static num min(num a, num b) => math.min(a, b); +} + +const IS_DART = true; + +scheduleMicroTask(Function fn) { + Zone.current.scheduleMicrotask(fn); +} + +bool isPresent(Object obj) => obj != null; +bool isBlank(Object obj) => obj == null; +bool isString(Object obj) => obj is String; +bool isFunction(Object obj) => obj is Function; +bool isType(Object obj) => obj is Type; +bool isStringMap(Object obj) => obj is Map; +bool isStrictStringMap(Object obj) => obj is Map; +bool isArray(Object obj) => obj is List; +bool isPromise(Object obj) => obj is Future; +bool isNumber(Object obj) => obj is num; +bool isBoolean(Object obj) => obj is bool; +bool isDate(Object obj) => obj is DateTime; + +String stringify(obj) { + final exp = new RegExp(r"from Function '(\w+)'"); + final str = obj.toString(); + if (exp.firstMatch(str) != null) { + return exp.firstMatch(str).group(1); + } else { + return str; + } +} + +int serializeEnum(val) { + return val.index; +} + +/** + * Deserializes an enum + * val should be the indexed value of the enum (sa returned from @Link{serializeEnum}) + * values should be a map from indexes to values for the enum that you want to deserialize. + */ +dynamic deserializeEnum(num val, Map values) { + return values[val]; +} + +String resolveEnumToken(enumValue, val) { + // turn Enum.Token -> Token + return val.toString().replaceFirst(new RegExp('^.+\\.'),''); +} + +class StringWrapper { + static String fromCharCode(int code) { + return new String.fromCharCode(code); + } + + static int charCodeAt(String s, int index) { + return s.codeUnitAt(index); + } + + static List split(String s, RegExp regExp) { + var parts = []; + var lastEnd = 0; + regExp.allMatches(s).forEach((match) { + parts.add(s.substring(lastEnd, match.start)); + lastEnd = match.end; + for (var i = 0; i < match.groupCount; i++) { + parts.add(match.group(i + 1)); + } + }); + parts.add(s.substring(lastEnd)); + return parts; + } + + static bool equals(String s, String s2) { + return s == s2; + } + + static String stripLeft(String s, String charVal) { + if (isPresent(s) && s.length > 0) { + var pos = 0; + for (var i = 0; i < s.length; i++) { + if (s[i] != charVal) break; + pos++; + } + s = s.substring(pos); + } + return s; + } + + static String stripRight(String s, String charVal) { + if (isPresent(s) && s.length > 0) { + var pos = s.length; + for (var i = s.length - 1; i >= 0; i--) { + if (s[i] != charVal) break; + pos--; + } + s = s.substring(0, pos); + } + return s; + } + + static String replace(String s, Pattern from, String replace) { + return s.replaceFirst(from, replace); + } + + static String replaceAll(String s, RegExp from, String replace) { + return s.replaceAll(from, replace); + } + + static String slice(String s, [int start = 0, int end]) { + start = _startOffset(s, start); + end = _endOffset(s, end); + //in JS if start > end an empty string is returned + if (end != null && start > end) { + return ""; + } + return s.substring(start, end); + } + + static String replaceAllMapped(String s, RegExp from, Function cb) { + return s.replaceAllMapped(from, cb); + } + + static bool contains(String s, String substr) { + return s.contains(substr); + } + + static int compare(String a, String b) => a.compareTo(b); + + // JS slice function can take start < 0 which indicates a position relative to + // the end of the string + static int _startOffset(String s, int start) { + int len = s.length; + return start < 0 ? math.max(len + start, 0) : math.min(start, len); + } + + // JS slice function can take end < 0 which indicates a position relative to + // the end of the string + static int _endOffset(String s, int end) { + int len = s.length; + if (end == null) return len; + return end < 0 ? math.max(len + end, 0) : math.min(end, len); + } +} + +class StringJoiner { + final List _parts = []; + + void add(String part) { + _parts.add(part); + } + + String toString() => _parts.join(""); +} + +class NumberWrapper { + static String toFixed(num n, int fractionDigits) { + return n.toStringAsFixed(fractionDigits); + } + + static bool equal(num a, num b) { + return a == b; + } + + static int parseIntAutoRadix(String text) { + return int.parse(text); + } + + static int parseInt(String text, int radix) { + return int.parse(text, radix: radix); + } + + static double parseFloat(String text) { + return double.parse(text); + } + + static double get NaN => double.NAN; + + static bool isNaN(num value) => value.isNaN; + + static bool isInteger(value) => value is int; +} + +class RegExpWrapper { + static RegExp create(regExpStr, [String flags = '']) { + bool multiLine = flags.contains('m'); + bool caseSensitive = !flags.contains('i'); + return new RegExp(regExpStr, + multiLine: multiLine, caseSensitive: caseSensitive); + } + + static Match firstMatch(RegExp regExp, String input) { + return regExp.firstMatch(input); + } + + static bool test(RegExp regExp, String input) { + return regExp.hasMatch(input); + } + + static Iterator matcher(RegExp regExp, String input) { + return regExp.allMatches(input).iterator; + } + + static String replaceAll(RegExp regExp, String input, Function replace) { + final m = RegExpWrapper.matcher(regExp, input); + var res = ""; + var prev = 0; + while(m.moveNext()) { + var c = m.current; + res += input.substring(prev, c.start); + res += replace(c); + prev = c.start + c[0].length; + } + res += input.substring(prev); + return res; + } +} + +class RegExpMatcherWrapper { + static _JSLikeMatch next(Iterator matcher) { + if (matcher.moveNext()) { + return new _JSLikeMatch(matcher.current); + } + return null; + } +} + +class _JSLikeMatch { + Match _m; + + _JSLikeMatch(this._m); + + String operator [](index) => _m[index]; + int get index => _m.start; + int get length => _m.groupCount + 1; +} + +class FunctionWrapper { + static apply(Function fn, posArgs) { + return Function.apply(fn, posArgs); + } + + static Function bind(Function fn, dynamic scope) { + return fn; + } +} + +const _NAN_KEY = const Object(); + +// Dart VM implements `identical` as true reference identity. JavaScript does +// not have this. The closest we have in JS is `===`. However, for strings JS +// would actually compare the contents rather than references. `dart2js` +// compiles `identical` to `===` and therefore there is a discrepancy between +// Dart VM and `dart2js`. The implementation of `looseIdentical` attempts to +// bridge the gap between the two while retaining good performance +// characteristics. In JS we use simple `identical`, which compiles to `===`, +// and in Dart VM we emulate the semantics of `===` by special-casing strings. +// Note that the VM check is a compile-time constant. This allows `dart2js` to +// evaluate the conditional during compilation and inline the entire function. +// +// See: dartbug.com/22496, dartbug.com/25270 +const _IS_DART_VM = !identical(1.0, 1); // a hack +bool looseIdentical(a, b) => _IS_DART_VM + ? _looseIdentical(a, b) + : identical(a, b); + +// This function is intentionally separated from `looseIdentical` to keep the +// number of AST nodes low enough for `dart2js` to inline the code. +bool _looseIdentical(a, b) => + a is String && b is String ? a == b : identical(a, b); + +// Dart compare map keys by equality and we can have NaN != NaN +dynamic getMapKey(value) { + if (value is! num) return value; + return value.isNaN ? _NAN_KEY : value; +} + +// TODO: remove with https://github.com/angular/angular/issues/3055 +dynamic normalizeBlank(obj) => obj; + +bool normalizeBool(bool obj) { + return isBlank(obj) ? false : obj; +} + +bool isJsObject(o) { + return false; +} + +warn(o) { + print(o); +} + +// Functions below are noop in Dart. Imperatively controlling dev mode kills +// tree shaking. We should only rely on `assertionsEnabled`. +@Deprecated('Do not use this function. It is for JS only. There is no alternative.') +void lockMode() {} +@Deprecated('Do not use this function. It is for JS only. There is no alternative.') +void enableDevMode() {} +@Deprecated('Do not use this function. It is for JS only. There is no alternative.') +void enableProdMode() {} + +/// Use this function to guard debugging code. When Dart is compiled in +/// production mode, the code guarded using this function will be tree +/// shaken away, reducing code size. +/// +/// WARNING: DO NOT CHANGE THIS METHOD! This method is designed to have no +/// more AST nodes than the maximum allowed by dart2js to inline it. In +/// addition, the use of `assert` allows the compiler to statically compute +/// the value returned by this function and tree shake conditions guarded by +/// it. +/// +/// Example: +/// +/// if (assertionsEnabled()) { +/// ...code here is tree shaken away in prod mode... +/// } +bool assertionsEnabled() { + var k = false; + assert((k = true)); + return k; +} + +// Can't be all uppercase as our transpiler would think it is a special directive... +class Json { + static parse(String s) => convert.JSON.decode(s); + static String stringify(data) { + var encoder = new convert.JsonEncoder.withIndent(" "); + return encoder.convert(data); + } +} + +class DateWrapper { + static DateTime create(int year, + [int month = 1, + int day = 1, + int hour = 0, + int minutes = 0, + int seconds = 0, + int milliseconds = 0]) { + return new DateTime(year, month, day, hour, minutes, seconds, milliseconds); + } + + static DateTime fromISOString(String str) { + return DateTime.parse(str); + } + + static DateTime fromMillis(int ms) { + return new DateTime.fromMillisecondsSinceEpoch(ms, isUtc: true); + } + + static int toMillis(DateTime date) { + return date.millisecondsSinceEpoch; + } + + static DateTime now() { + return new DateTime.now(); + } + + static String toJson(DateTime date) { + return date.toUtc().toIso8601String(); + } +} + +bool isPrimitive(Object obj) => obj is num || obj is bool || obj == null || obj is String; + +// needed to match the exports from lang.js +var global = null; + +dynamic evalExpression(String sourceUrl, String expr, String declarations, Map vars) { + throw "Dart does not support evaluating expression during runtime!"; +} + +bool hasConstructor(Object value, Type type) { + return value.runtimeType == type; +} + +num bitWiseOr(List values) { + var val = values.reduce((num a, num b) => (a as int) | (b as int)); + return val as num; +} + +num bitWiseAnd(List values) { + var val = values.reduce((num a, num b) => (a as int) & (b as int)); + return val as num; +} + +String escape(String s) { + return Uri.encodeComponent(s); +} diff --git a/modules/@angular/forms/src/facade/lang.ts b/modules/@angular/forms/src/facade/lang.ts new file mode 100644 index 0000000000..b93803ef3f --- /dev/null +++ b/modules/@angular/forms/src/facade/lang.ts @@ -0,0 +1,502 @@ +export interface BrowserNodeGlobal { + Object: typeof Object; + Array: typeof Array; + Map: typeof Map; + Set: typeof Set; + Date: DateConstructor; + RegExp: RegExpConstructor; + JSON: typeof JSON; + Math: any; // typeof Math; + assert(condition: any): void; + Reflect: any; + getAngularTestability: Function; + getAllAngularTestabilities: Function; + getAllAngularRootElements: Function; + frameworkStabilizers: Array; + setTimeout: Function; + clearTimeout: Function; + setInterval: Function; + clearInterval: Function; + encodeURI: Function; +} + +// TODO(jteplitz602): Load WorkerGlobalScope from lib.webworker.d.ts file #3492 +declare var WorkerGlobalScope: any /** TODO #9100 */; +// CommonJS / Node have global context exposed as "global" variable. +// We don't want to include the whole node.d.ts this this compilation unit so we'll just fake +// the global "global" var for now. +declare var global: any /** TODO #9100 */; + +var globalScope: BrowserNodeGlobal; +if (typeof window === 'undefined') { + if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) { + // TODO: Replace any with WorkerGlobalScope from lib.webworker.d.ts #3492 + globalScope = self; + } else { + globalScope = global; + } +} else { + globalScope = window; +} + +export function scheduleMicroTask(fn: Function) { + Zone.current.scheduleMicroTask('scheduleMicrotask', fn); +} + +export const IS_DART = false; + +// Need to declare a new variable for global here since TypeScript +// exports the original value of the symbol. +var _global: BrowserNodeGlobal = globalScope; + +export {_global as global}; + +export var Type = Function; + +/** + * Runtime representation a type that a Component or other object is instances of. + * + * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is be represented by + * the `MyCustomComponent` constructor function. + */ +export interface Type extends Function {} + +/** + * Runtime representation of a type that is constructable (non-abstract). + */ +export interface ConcreteType extends Type { new (...args: any[] /** TODO #9100 */): any; } + +export function getTypeNameForDebugging(type: Type): string { + if (type['name']) { + return type['name']; + } + return typeof type; +} + + +export var Math = _global.Math; +export var Date = _global.Date; + +var _devMode: boolean = true; +var _modeLocked: boolean = false; + +export function lockMode() { + _modeLocked = true; +} + +/** + * Disable Angular's development mode, which turns off assertions and other + * checks within the framework. + * + * One important assertion this disables verifies that a change detection pass + * does not result in additional changes to any bindings (also known as + * unidirectional data flow). + * @stable + */ +export function enableProdMode() { + if (_modeLocked) { + // Cannot use BaseException as that ends up importing from facade/lang. + throw 'Cannot enable prod mode after platform setup.'; + } + _devMode = false; +} + +export function assertionsEnabled(): boolean { + return _devMode; +} + +// TODO: remove calls to assert in production environment +// Note: Can't just export this and import in in other files +// as `assert` is a reserved keyword in Dart +_global.assert = function assert(condition) { + // TODO: to be fixed properly via #2830, noop for now +}; + +export function isPresent(obj: any): boolean { + return obj !== undefined && obj !== null; +} + +export function isBlank(obj: any): boolean { + return obj === undefined || obj === null; +} + +export function isBoolean(obj: any): boolean { + return typeof obj === 'boolean'; +} + +export function isNumber(obj: any): boolean { + return typeof obj === 'number'; +} + +export function isString(obj: any): obj is String { + return typeof obj === 'string'; +} + +export function isFunction(obj: any): boolean { + return typeof obj === 'function'; +} + +export function isType(obj: any): boolean { + return isFunction(obj); +} + +export function isStringMap(obj: any): boolean { + return typeof obj === 'object' && obj !== null; +} + +const STRING_MAP_PROTO = Object.getPrototypeOf({}); +export function isStrictStringMap(obj: any): boolean { + return isStringMap(obj) && Object.getPrototypeOf(obj) === STRING_MAP_PROTO; +} + +export function isPromise(obj: any): boolean { + return obj instanceof (_global).Promise; +} + +export function isArray(obj: any): boolean { + return Array.isArray(obj); +} + +export function isDate(obj: any): obj is Date { + return obj instanceof Date && !isNaN(obj.valueOf()); +} + +export function noop() {} + +export function stringify(token: any): string { + if (typeof token === 'string') { + return token; + } + + if (token === undefined || token === null) { + return '' + token; + } + + if (token.name) { + return token.name; + } + if (token.overriddenName) { + return token.overriddenName; + } + + var res = token.toString(); + var newLineIndex = res.indexOf('\n'); + return (newLineIndex === -1) ? res : res.substring(0, newLineIndex); +} + +// serialize / deserialize enum exist only for consistency with dart API +// enums in typescript don't need to be serialized + +export function serializeEnum(val: any): number { + return val; +} + +export function deserializeEnum(val: any, values: Map): any { + return val; +} + +export function resolveEnumToken(enumValue: any, val: any): string { + return enumValue[val]; +} + +export class StringWrapper { + static fromCharCode(code: number): string { return String.fromCharCode(code); } + + static charCodeAt(s: string, index: number): number { return s.charCodeAt(index); } + + static split(s: string, regExp: RegExp): string[] { return s.split(regExp); } + + static equals(s: string, s2: string): boolean { return s === s2; } + + static stripLeft(s: string, charVal: string): string { + if (s && s.length) { + var pos = 0; + for (var i = 0; i < s.length; i++) { + if (s[i] != charVal) break; + pos++; + } + s = s.substring(pos); + } + return s; + } + + static stripRight(s: string, charVal: string): string { + if (s && s.length) { + var pos = s.length; + for (var i = s.length - 1; i >= 0; i--) { + if (s[i] != charVal) break; + pos--; + } + s = s.substring(0, pos); + } + return s; + } + + static replace(s: string, from: string, replace: string): string { + return s.replace(from, replace); + } + + static replaceAll(s: string, from: RegExp, replace: string): string { + return s.replace(from, replace); + } + + static slice(s: string, from: number = 0, to: number = null): string { + return s.slice(from, to === null ? undefined : to); + } + + static replaceAllMapped(s: string, from: RegExp, cb: Function): string { + return s.replace(from, function(...matches: any[]) { + // Remove offset & string from the result array + matches.splice(-2, 2); + // The callback receives match, p1, ..., pn + return cb(matches); + }); + } + + static contains(s: string, substr: string): boolean { return s.indexOf(substr) != -1; } + + static compare(a: string, b: string): number { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } + } +} + +export class StringJoiner { + constructor(public parts: string[] = []) {} + + add(part: string): void { this.parts.push(part); } + + toString(): string { return this.parts.join(''); } +} + +export class NumberParseError extends Error { + name: string; + + constructor(public message: string) { super(); } + + toString(): string { return this.message; } +} + + +export class NumberWrapper { + static toFixed(n: number, fractionDigits: number): string { return n.toFixed(fractionDigits); } + + static equal(a: number, b: number): boolean { return a === b; } + + static parseIntAutoRadix(text: string): number { + var result: number = parseInt(text); + if (isNaN(result)) { + throw new NumberParseError('Invalid integer literal when parsing ' + text); + } + return result; + } + + static parseInt(text: string, radix: number): number { + if (radix == 10) { + if (/^(\-|\+)?[0-9]+$/.test(text)) { + return parseInt(text, radix); + } + } else if (radix == 16) { + if (/^(\-|\+)?[0-9ABCDEFabcdef]+$/.test(text)) { + return parseInt(text, radix); + } + } else { + var result: number = parseInt(text, radix); + if (!isNaN(result)) { + return result; + } + } + throw new NumberParseError( + 'Invalid integer literal when parsing ' + text + ' in base ' + radix); + } + + // TODO: NaN is a valid literal but is returned by parseFloat to indicate an error. + static parseFloat(text: string): number { return parseFloat(text); } + + static get NaN(): number { return NaN; } + + static isNaN(value: any): boolean { return isNaN(value); } + + static isInteger(value: any): boolean { return Number.isInteger(value); } +} + +export var RegExp = _global.RegExp; + +export class RegExpWrapper { + static create(regExpStr: string, flags: string = ''): RegExp { + flags = flags.replace(/g/g, ''); + return new _global.RegExp(regExpStr, flags + 'g'); + } + static firstMatch(regExp: RegExp, input: string): RegExpExecArray { + // Reset multimatch regex state + regExp.lastIndex = 0; + return regExp.exec(input); + } + static test(regExp: RegExp, input: string): boolean { + regExp.lastIndex = 0; + return regExp.test(input); + } + static matcher(regExp: RegExp, input: string): {re: RegExp; input: string} { + // Reset regex state for the case + // someone did not loop over all matches + // last time. + regExp.lastIndex = 0; + return {re: regExp, input: input}; + } + static replaceAll(regExp: RegExp, input: string, replace: Function): string { + let c = regExp.exec(input); + let res = ''; + regExp.lastIndex = 0; + let prev = 0; + while (c) { + res += input.substring(prev, c.index); + res += replace(c); + prev = c.index + c[0].length; + regExp.lastIndex = prev; + c = regExp.exec(input); + } + res += input.substring(prev); + return res; + } +} + +export class RegExpMatcherWrapper { + static next(matcher: {re: RegExp; input: string}): RegExpExecArray { + return matcher.re.exec(matcher.input); + } +} + +export class FunctionWrapper { + static apply(fn: Function, posArgs: any): any { return fn.apply(null, posArgs); } + + static bind(fn: Function, scope: any): Function { return fn.bind(scope); } +} + +// JS has NaN !== NaN +export function looseIdentical(a: any, b: any): boolean { + return a === b || typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b); +} + +// JS considers NaN is the same as NaN for map Key (while NaN !== NaN otherwise) +// see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map +export function getMapKey(value: T): T { + return value; +} + +export function normalizeBlank(obj: Object): any { + return isBlank(obj) ? null : obj; +} + +export function normalizeBool(obj: boolean): boolean { + return isBlank(obj) ? false : obj; +} + +export function isJsObject(o: any): boolean { + return o !== null && (typeof o === 'function' || typeof o === 'object'); +} + +export function print(obj: Error | Object) { + console.log(obj); +} + +export function warn(obj: Error | Object) { + console.warn(obj); +} + +// Can't be all uppercase as our transpiler would think it is a special directive... +export class Json { + static parse(s: string): Object { return _global.JSON.parse(s); } + static stringify(data: Object): string { + // Dart doesn't take 3 arguments + return _global.JSON.stringify(data, null, 2); + } +} + +export class DateWrapper { + static create( + year: number, month: number = 1, day: number = 1, hour: number = 0, minutes: number = 0, + seconds: number = 0, milliseconds: number = 0): Date { + return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds); + } + static fromISOString(str: string): Date { return new Date(str); } + static fromMillis(ms: number): Date { return new Date(ms); } + static toMillis(date: Date): number { return date.getTime(); } + static now(): Date { return new Date(); } + static toJson(date: Date): string { return date.toJSON(); } +} + +export function setValueOnPath(global: any, path: string, value: any) { + var parts = path.split('.'); + var obj: any = global; + while (parts.length > 1) { + var name = parts.shift(); + if (obj.hasOwnProperty(name) && isPresent(obj[name])) { + obj = obj[name]; + } else { + obj = obj[name] = {}; + } + } + if (obj === undefined || obj === null) { + obj = {}; + } + obj[parts.shift()] = value; +} + +// When Symbol.iterator doesn't exist, retrieves the key used in es6-shim +declare var Symbol: any; +var _symbolIterator: any = null; +export function getSymbolIterator(): string|symbol { + if (isBlank(_symbolIterator)) { + if (isPresent((globalScope).Symbol) && isPresent(Symbol.iterator)) { + _symbolIterator = Symbol.iterator; + } else { + // es6-shim specific logic + var keys = Object.getOwnPropertyNames(Map.prototype); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + if (key !== 'entries' && key !== 'size' && + (Map as any).prototype[key] === Map.prototype['entries']) { + _symbolIterator = key; + } + } + } + } + return _symbolIterator; +} + +export function evalExpression( + sourceUrl: string, expr: string, declarations: string, vars: {[key: string]: any}): any { + var fnBody = `${declarations}\nreturn ${expr}\n//# sourceURL=${sourceUrl}`; + var fnArgNames: string[] = []; + var fnArgValues: any[] = []; + for (var argName in vars) { + fnArgNames.push(argName); + fnArgValues.push(vars[argName]); + } + return new Function(...fnArgNames.concat(fnBody))(...fnArgValues); +} + +export function isPrimitive(obj: any): boolean { + return !isJsObject(obj); +} + +export function hasConstructor(value: Object, type: Type): boolean { + return value.constructor === type; +} + +export function bitWiseOr(values: number[]): number { + return values.reduce((a, b) => { return a | b; }); +} + +export function bitWiseAnd(values: number[]): number { + return values.reduce((a, b) => { return a & b; }); +} + +export function escape(s: string): string { + return _global.encodeURI(s); +} diff --git a/modules/@angular/forms/src/facade/math.dart b/modules/@angular/forms/src/facade/math.dart new file mode 100644 index 0000000000..08adb680d3 --- /dev/null +++ b/modules/@angular/forms/src/facade/math.dart @@ -0,0 +1,24 @@ +library angular.core.facade.math; + +import 'dart:core' show double, num; +import 'dart:math' as math; + +const NaN = double.NAN; + +class Math { + static num pow(num x, num exponent) { + return math.pow(x, exponent); + } + + static num max(num a, num b) => math.max(a, b); + + static num min(num a, num b) => math.min(a, b); + + static num floor(num a) => a.floor(); + + static num ceil(num a) => a.ceil(); + + static num sqrt(num x) => math.sqrt(x); + + static num round(num x) => x.round(); +} diff --git a/modules/@angular/forms/src/facade/math.ts b/modules/@angular/forms/src/facade/math.ts new file mode 100644 index 0000000000..7b191cf5c4 --- /dev/null +++ b/modules/@angular/forms/src/facade/math.ts @@ -0,0 +1,4 @@ +import {global} from './lang'; + +export var Math = global.Math; +export var NaN: any /** TODO #???? */ = typeof NaN; diff --git a/modules/@angular/forms/src/facade/promise.dart b/modules/@angular/forms/src/facade/promise.dart new file mode 100644 index 0000000000..eda21d483a --- /dev/null +++ b/modules/@angular/forms/src/facade/promise.dart @@ -0,0 +1,58 @@ +library angular2.core.facade.promise; + +import 'dart:async'; +import 'dart:async' as async; + +class PromiseWrapper { + static Future/**/ resolve/**/(dynamic /*=T*/ obj) => new Future.value(obj); + + static Future/**/ reject/**/(dynamic /*=T*/ obj, Object stackTrace) => new Future.error(obj, + stackTrace != null ? stackTrace : obj is Error ? (obj as Error).stackTrace : null); + + static Future*/> all/**/(List promises) { + return Future + .wait(promises.map((p) => p is Future ? p as Future/**/ : new Future/**/.value(p))); + } + static Future/**/ then/**/(Future/**/ promise, dynamic /*=R*/ success(dynamic /*=T*/ value), [Function onError]) { + if (success == null) return promise.catchError(onError); + return promise.then(success, onError: onError); + } + + static Future/**/ wrap/**/(dynamic /*=T*/ fn()) { + return new Future(fn); + } + + // Note: We can't rename this method to `catch`, as this is not a valid + // method name in Dart. + static Future catchError(Future promise, Function onError) { + return promise.catchError(onError); + } + + static void scheduleMicrotask(fn) { + async.scheduleMicrotask(fn); + } + + static bool isPromise(obj) { + return obj is Future; + } + + static PromiseCompleter/**/ completer/**/() => + new PromiseCompleter(); +} + +class PromiseCompleter { + final Completer c = new Completer(); + + Future get promise => c.future; + + void resolve(v) { + c.complete(v); + } + + void reject(error, stack) { + if (stack == null && error is Error) { + stack = error.stackTrace; + } + c.completeError(error, stack); + } +} diff --git a/modules/@angular/forms/src/facade/promise.js b/modules/@angular/forms/src/facade/promise.js new file mode 100644 index 0000000000..83930f30c2 --- /dev/null +++ b/modules/@angular/forms/src/facade/promise.js @@ -0,0 +1,49 @@ +"use strict"; +var PromiseCompleter = (function () { + function PromiseCompleter() { + var _this = this; + this.promise = new Promise(function (res, rej) { + _this.resolve = res; + _this.reject = rej; + }); + } + return PromiseCompleter; +}()); +exports.PromiseCompleter = PromiseCompleter; +var PromiseWrapper = (function () { + function PromiseWrapper() { + } + PromiseWrapper.resolve = function (obj) { return Promise.resolve(obj); }; + PromiseWrapper.reject = function (obj, _) { return Promise.reject(obj); }; + // Note: We can't rename this method into `catch`, as this is not a valid + // method name in Dart. + PromiseWrapper.catchError = function (promise, onError) { + return promise.catch(onError); + }; + PromiseWrapper.all = function (promises) { + if (promises.length == 0) + return Promise.resolve([]); + return Promise.all(promises); + }; + PromiseWrapper.then = function (promise, success, rejection) { + return promise.then(success, rejection); + }; + PromiseWrapper.wrap = function (computation) { + return new Promise(function (res, rej) { + try { + res(computation()); + } + catch (e) { + rej(e); + } + }); + }; + PromiseWrapper.scheduleMicrotask = function (computation) { + PromiseWrapper.then(PromiseWrapper.resolve(null), computation, function (_) { }); + }; + PromiseWrapper.isPromise = function (obj) { return obj instanceof Promise; }; + PromiseWrapper.completer = function () { return new PromiseCompleter(); }; + return PromiseWrapper; +}()); +exports.PromiseWrapper = PromiseWrapper; +//# sourceMappingURL=promise.js.map \ No newline at end of file diff --git a/modules/@angular/forms/src/facade/promise.ts b/modules/@angular/forms/src/facade/promise.ts new file mode 100644 index 0000000000..5747a209a9 --- /dev/null +++ b/modules/@angular/forms/src/facade/promise.ts @@ -0,0 +1,55 @@ + +export class PromiseCompleter { + promise: Promise; + resolve: (value?: R|PromiseLike) => void; + reject: (error?: any, stackTrace?: string) => void; + + constructor() { + this.promise = new Promise((res, rej) => { + this.resolve = res; + this.reject = rej; + }); + } +} + +export class PromiseWrapper { + static resolve(obj: T): Promise { return Promise.resolve(obj); } + + static reject(obj: any, _: any): Promise { return Promise.reject(obj); } + + // Note: We can't rename this method into `catch`, as this is not a valid + // method name in Dart. + static catchError(promise: Promise, onError: (error: any) => T | PromiseLike): + Promise { + return promise.catch(onError); + } + + static all(promises: (T|Promise)[]): Promise { + if (promises.length == 0) return Promise.resolve([]); + return Promise.all(promises); + } + + static then( + promise: Promise, success: (value: T) => U | PromiseLike, + rejection?: (error: any, stack?: any) => U | PromiseLike): Promise { + return promise.then(success, rejection); + } + + static wrap(computation: () => T): Promise { + return new Promise((res, rej) => { + try { + res(computation()); + } catch (e) { + rej(e); + } + }); + } + + static scheduleMicrotask(computation: () => any): void { + PromiseWrapper.then(PromiseWrapper.resolve(null), computation, (_) => {}); + } + + static isPromise(obj: any): boolean { return obj instanceof Promise; } + + static completer(): PromiseCompleter { return new PromiseCompleter(); } +} diff --git a/modules/@angular/common/src/forms/form_builder.ts b/modules/@angular/forms/src/form_builder.ts similarity index 97% rename from modules/@angular/common/src/forms/form_builder.ts rename to modules/@angular/forms/src/form_builder.ts index 46288f89aa..56be457a7e 100644 --- a/modules/@angular/common/src/forms/form_builder.ts +++ b/modules/@angular/forms/src/form_builder.ts @@ -1,9 +1,8 @@ import {Injectable} from '@angular/core'; -import {StringMapWrapper} from '../facade/collection'; -import {isArray, isPresent} from '../facade/lang'; - import {AsyncValidatorFn, ValidatorFn} from './directives/validators'; +import {StringMapWrapper} from './facade/collection'; +import {isArray, isPresent} from './facade/lang'; import * as modelModule from './model'; diff --git a/modules/@angular/forms/src/form_providers.ts b/modules/@angular/forms/src/form_providers.ts new file mode 100644 index 0000000000..733465e39d --- /dev/null +++ b/modules/@angular/forms/src/form_providers.ts @@ -0,0 +1,54 @@ + +import {COMMON_DIRECTIVES, FORM_DIRECTIVES as OLD_FORM_DIRECTIVES, FORM_PROVIDERS as OLD_FORM_PROVIDERS} from '@angular/common'; +import {CompilerConfig} from '@angular/compiler'; +import {PLATFORM_DIRECTIVES, PLATFORM_PIPES, Type} from '@angular/core'; + +import {FORM_DIRECTIVES as NEW_FORM_DIRECTIVES} from './directives'; +import {RadioControlRegistry as NewRadioControlRegistry} from './directives/radio_control_value_accessor'; +import {ListWrapper} from './facade/collection'; +import {FormBuilder as NewFormBuilder} from './form_builder'; + + + +/* + * Shorthand set of providers used for building Angular forms. + * + * ### Example + * + * ```typescript + * bootstrap(MyApp, [FORM_PROVIDERS]); + * ``` + * + * @experimental + */ +export const FORM_PROVIDERS: Type[] = /*@ts2dart_const*/[NewFormBuilder, NewRadioControlRegistry]; + +function flatten(platformDirectives: any[]): any[] { + let flattenedDirectives: any[] = []; + platformDirectives.forEach((directives) => { + if (Array.isArray(directives)) { + flattenedDirectives = flattenedDirectives.concat(directives); + } else { + flattenedDirectives.push(directives); + } + }); + return flattenedDirectives; +} + +export function disableDeprecatedForms(): any[] { + return [{ + provide: CompilerConfig, + useFactory: (platformDirectives: any[], platformPipes: any[]) => { + const flattenedDirectives = flatten(platformDirectives); + ListWrapper.remove(flattenedDirectives, OLD_FORM_DIRECTIVES); + return new CompilerConfig({platformDirectives: flattenedDirectives, platformPipes}); + }, + deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] + }]; +} + +export function provideForms(): any[] { + return [ + {provide: PLATFORM_DIRECTIVES, useValue: NEW_FORM_DIRECTIVES, multi: true}, FORM_PROVIDERS + ]; +} diff --git a/modules/@angular/forms/src/forms.ts b/modules/@angular/forms/src/forms.ts new file mode 100644 index 0000000000..8ba67ef723 --- /dev/null +++ b/modules/@angular/forms/src/forms.ts @@ -0,0 +1,37 @@ +/** + * @module + * @description + * This module is used for handling user input, by defining and building a {@link FormGroup} that + * consists of + * {@link FormControl} objects, and mapping them onto the DOM. {@link FormControl} objects can then + * be used + * to read information + * from the form DOM elements. + * + * Forms providers are not included in default providers; you must import these providers + * explicitly. + */ + + +export {FORM_DIRECTIVES, REACTIVE_FORM_DIRECTIVES, RadioButtonState} from './directives'; +export {AbstractControlDirective} from './directives/abstract_control_directive'; +export {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; +export {ControlContainer} from './directives/control_container'; +export {ControlValueAccessor, NG_VALUE_ACCESSOR} from './directives/control_value_accessor'; +export {DefaultValueAccessor} from './directives/default_value_accessor'; +export {Form} from './directives/form_interface'; +export {NgControl} from './directives/ng_control'; +export {NgControlStatus} from './directives/ng_control_status'; +export {NgForm} from './directives/ng_form'; +export {NgModel} from './directives/ng_model'; +export {NgModelGroup} from './directives/ng_model_group'; +export {FormControlDirective} from './directives/reactive_directives/form_control_directive'; +export {FormControlName} from './directives/reactive_directives/form_control_name'; +export {FormGroupDirective} from './directives/reactive_directives/form_group_directive'; +export {FormGroupName} from './directives/reactive_directives/form_group_name'; +export {NgSelectOption, SelectControlValueAccessor} from './directives/select_control_value_accessor'; +export {MaxLengthValidator, MinLengthValidator, PatternValidator, RequiredValidator, Validator} from './directives/validators'; +export {FormBuilder} from './form_builder'; +export {AbstractControl, FormArray, FormControl, FormGroup} from './model'; +export {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from './validators'; +export * from './form_providers'; diff --git a/modules/@angular/common/src/forms/model.ts b/modules/@angular/forms/src/model.ts similarity index 98% rename from modules/@angular/common/src/forms/model.ts rename to modules/@angular/forms/src/model.ts index 618b3c945b..df183d6fdb 100644 --- a/modules/@angular/common/src/forms/model.ts +++ b/modules/@angular/forms/src/model.ts @@ -1,10 +1,9 @@ -import {EventEmitter, Observable, ObservableWrapper} from '../facade/async'; -import {ListWrapper, StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent, normalizeBool} from '../facade/lang'; -import {PromiseWrapper} from '../facade/promise'; - import {composeAsyncValidators, composeValidators} from './directives/shared'; import {AsyncValidatorFn, ValidatorFn} from './directives/validators'; +import {EventEmitter, Observable, ObservableWrapper} from './facade/async'; +import {ListWrapper, StringMapWrapper} from './facade/collection'; +import {isBlank, isPresent, normalizeBool} from './facade/lang'; +import {PromiseWrapper} from './facade/promise'; diff --git a/modules/@angular/common/src/forms/validators.ts b/modules/@angular/forms/src/validators.ts similarity index 95% rename from modules/@angular/common/src/forms/validators.ts rename to modules/@angular/forms/src/validators.ts index 84b15df228..4d740e17a4 100644 --- a/modules/@angular/common/src/forms/validators.ts +++ b/modules/@angular/forms/src/validators.ts @@ -1,14 +1,14 @@ import {OpaqueToken} from '@angular/core'; -import {ObservableWrapper} from '../facade/async'; -import {StringMapWrapper} from '../facade/collection'; -import {isBlank, isPresent, isString} from '../facade/lang'; -import {PromiseWrapper} from '../facade/promise'; - import {AsyncValidatorFn, ValidatorFn} from './directives/validators'; +import {ObservableWrapper} from './facade/async'; +import {StringMapWrapper} from './facade/collection'; +import {isBlank, isPresent, isString} from './facade/lang'; +import {PromiseWrapper} from './facade/promise'; import * as modelModule from './model'; + /** * Providers for validators to be used for {@link FormControl}s in a form. * diff --git a/modules/@angular/common/test/forms/directives_spec.ts b/modules/@angular/forms/test/directives_spec.ts similarity index 98% rename from modules/@angular/common/test/forms/directives_spec.ts rename to modules/@angular/forms/test/directives_spec.ts index 97c49e505f..eb4b26b603 100644 --- a/modules/@angular/common/test/forms/directives_spec.ts +++ b/modules/@angular/forms/test/directives_spec.ts @@ -2,13 +2,13 @@ import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit import {fakeAsync, flushMicrotasks, Log, tick,} from '@angular/core/testing'; -import {SpyNgControl, SpyValueAccessor} from '../spies'; +import {SpyNgControl, SpyValueAccessor} from './spies'; -import {FormGroup, FormControl, FormControlName, FormGroupName, NgModelGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/common/src/forms'; +import {FormGroup, FormControl, FormControlName, FormGroupName, NgModelGroup, FormGroupDirective, ControlValueAccessor, Validators, NgForm, NgModel, FormControlDirective, NgControl, DefaultValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, Validator} from '@angular/forms'; -import {selectValueAccessor, composeValidators} from '@angular/common/src/forms/directives/shared'; -import {TimerWrapper} from '../../src/facade/async'; -import {PromiseWrapper} from '../../src/facade/promise'; +import {selectValueAccessor, composeValidators} from '@angular/forms/src/directives/shared'; +import {TimerWrapper} from '../src/facade/async'; +import {PromiseWrapper} from '../src/facade/promise'; import {SimpleChange} from '@angular/core/src/change_detection'; class DummyControlValueAccessor implements ControlValueAccessor { diff --git a/modules/@angular/common/test/forms/form_builder_spec.ts b/modules/@angular/forms/test/form_builder_spec.ts similarity index 94% rename from modules/@angular/common/test/forms/form_builder_spec.ts rename to modules/@angular/forms/test/form_builder_spec.ts index 90fd4cf819..d31557df91 100644 --- a/modules/@angular/common/test/forms/form_builder_spec.ts +++ b/modules/@angular/forms/test/form_builder_spec.ts @@ -1,7 +1,7 @@ -import {FormBuilder, FormControl} from '@angular/common/src/forms'; import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {FormBuilder, FormControl} from '@angular/forms'; -import {PromiseWrapper} from '../../src/facade/promise'; +import {PromiseWrapper} from '../src/facade/promise'; export function main() { function syncValidator(_: any /** TODO #9100 */): any /** TODO #9100 */ { return null; } diff --git a/modules/@angular/common/test/forms/integration_spec.ts b/modules/@angular/forms/test/integration_spec.ts similarity index 57% rename from modules/@angular/common/test/forms/integration_spec.ts rename to modules/@angular/forms/test/integration_spec.ts index 53214614b1..ba032ccaa9 100644 --- a/modules/@angular/common/test/forms/integration_spec.ts +++ b/modules/@angular/forms/test/integration_spec.ts @@ -1,5 +1,4 @@ import {NgFor, NgIf} from '@angular/common'; -import {ControlValueAccessor, FORM_DIRECTIVES, FORM_PROVIDERS, FormControl, FormGroup, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, NgModel, REACTIVE_FORM_DIRECTIVES, RadioButtonState, Validator, Validators} from '@angular/common/src/forms'; import {TestComponentBuilder} from '@angular/compiler/testing'; import {ComponentFixture} from '@angular/compiler/testing'; import {Component, Directive, EventEmitter, Output} from '@angular/core'; @@ -7,17 +6,19 @@ import {Input, Provider, forwardRef} from '@angular/core'; import {fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; +import {ControlValueAccessor, FORM_DIRECTIVES, FORM_PROVIDERS, FormControl, FormGroup, NG_ASYNC_VALIDATORS, NG_VALIDATORS, NgControl, NgForm, NgModel, REACTIVE_FORM_DIRECTIVES, RadioButtonState, Validator, Validators, disableDeprecatedForms, provideForms} from '@angular/forms'; import {By} from '@angular/platform-browser/src/dom/debug/by'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {dispatchEvent} from '@angular/platform-browser/testing'; -import {ObservableWrapper, TimerWrapper} from '../../src/facade/async'; -import {ListWrapper} from '../../src/facade/collection'; -import {PromiseWrapper} from '../../src/facade/promise'; +import {ObservableWrapper, TimerWrapper} from '../src/facade/async'; +import {ListWrapper} from '../src/facade/collection'; +import {PromiseWrapper} from '../src/facade/promise'; export function main() { - // TODO(kara): Turn these tests on in CI when we flip the switch on new forms module - xdescribe('integration tests', () => { + describe('integration tests', () => { + let providerArr: any[]; + beforeEach(() => { providerArr = [disableDeprecatedForms(), provideForms()]; }); it('should initialize DOM elements with the given form object', inject( @@ -27,15 +28,18 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'login': new FormControl('loginValue')}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'login': new FormControl('loginValue')}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('loginValue'); - async.done(); - }); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('loginValue'); + async.done(); + }); })); it('should throw if a form isn\'t passed into formGroup', @@ -46,11 +50,14 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - expect(() => fixture.detectChanges()) - .toThrowError(new RegExp(`formGroup expects a FormGroup instance`)); - async.done(); - }); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + expect(() => fixture.detectChanges()) + .toThrowError(new RegExp(`formGroup expects a FormGroup instance`)); + async.done(); + }); })); it('should update the control group values on DOM change', @@ -63,17 +70,20 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('input')); - input.nativeElement.value = 'updatedValue'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = 'updatedValue'; + dispatchEvent(input.nativeElement, 'input'); - expect(form.value).toEqual({'login': 'updatedValue'}); - async.done(); - }); + expect(form.value).toEqual({'login': 'updatedValue'}); + async.done(); + }); })); it('should ignore the change event for ', @@ -86,19 +96,22 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('input')); - input.nativeElement.value = 'updatedValue'; + input.nativeElement.value = 'updatedValue'; - ObservableWrapper.subscribe( - form.valueChanges, (value) => { throw 'Should not happen'; }); - dispatchEvent(input.nativeElement, 'change'); + ObservableWrapper.subscribe( + form.valueChanges, (value) => { throw 'Should not happen'; }); + dispatchEvent(input.nativeElement, 'change'); - async.done(); - }); + async.done(); + }); })); it('should emit ngSubmit event on submit', @@ -182,19 +195,22 @@ export function main() { const t = `
`; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = control; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = control; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('loginValue'); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('loginValue'); - input.nativeElement.value = 'updatedValue'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = 'updatedValue'; + dispatchEvent(input.nativeElement, 'input'); - expect(control.value).toEqual('updatedValue'); - async.done(); - }); + expect(control.value).toEqual('updatedValue'); + async.done(); + }); })); it('should update DOM elements when rebinding the control group', @@ -205,19 +221,22 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'login': new FormControl('oldValue')}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'login': new FormControl('oldValue')}); + fixture.detectChanges(); - fixture.debugElement.componentInstance.form = - new FormGroup({'login': new FormControl('newValue')}); - fixture.detectChanges(); + fixture.debugElement.componentInstance.form = + new FormGroup({'login': new FormControl('newValue')}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('newValue'); - async.done(); - }); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('newValue'); + async.done(); + }); })); it('should update DOM elements when updating the value of a control', @@ -231,18 +250,21 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - login.updateValue('newValue'); + login.updateValue('newValue'); - fixture.detectChanges(); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('newValue'); - async.done(); - }); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('newValue'); + async.done(); + }); })); it('should mark controls as touched after interacting with the DOM control', @@ -256,19 +278,22 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - var loginEl = fixture.debugElement.query(By.css('input')); - expect(login.touched).toBe(false); + var loginEl = fixture.debugElement.query(By.css('input')); + expect(login.touched).toBe(false); - dispatchEvent(loginEl.nativeElement, 'blur'); + dispatchEvent(loginEl.nativeElement, 'blur'); - expect(login.touched).toBe(true); + expect(login.touched).toBe(true); - async.done(); - }); + async.done(); + }); })); describe('different control types', () => { @@ -280,20 +305,25 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'text': new FormControl('old')}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'text': new FormControl('old')}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('old'); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('old'); - input.nativeElement.value = 'new'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = 'new'; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'text': 'new'}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'text': 'new' + }); + async.done(); + }); })); it('should support without type', @@ -304,19 +334,24 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'text': new FormControl('old')}); - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('old'); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'text': new FormControl('old')}); + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('old'); - input.nativeElement.value = 'new'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = 'new'; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'text': 'new'}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'text': 'new' + }); + async.done(); + }); })); it('should support `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'text': new FormControl('old')}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'text': new FormControl('old')}); + fixture.detectChanges(); - var textarea = fixture.debugElement.query(By.css('textarea')); - expect(textarea.nativeElement.value).toEqual('old'); + var textarea = fixture.debugElement.query(By.css('textarea')); + expect(textarea.nativeElement.value).toEqual('old'); - textarea.nativeElement.value = 'new'; - dispatchEvent(textarea.nativeElement, 'input'); + textarea.nativeElement.value = 'new'; + dispatchEvent(textarea.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'text': 'new'}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'text': 'new' + }); + async.done(); + }); })); it('should support ', @@ -351,22 +391,25 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'checkbox': new FormControl(true)}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'checkbox': new FormControl(true)}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.checked).toBe(true); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.checked).toBe(true); - input.nativeElement.checked = false; - dispatchEvent(input.nativeElement, 'change'); + input.nativeElement.checked = false; + dispatchEvent(input.nativeElement, 'change'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({ - 'checkbox': false - }); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'checkbox': false + }); + async.done(); + }); })); it('should support ', @@ -377,20 +420,23 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'num': new FormControl(10)}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'num': new FormControl(10)}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('10'); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('10'); - input.nativeElement.value = '20'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = '20'; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'num': 20}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({'num': 20}); + async.done(); + }); })); it('should support when value is cleared in the UI', @@ -401,25 +447,30 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'num': new FormControl(10)}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'num': new FormControl(10)}); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - input.nativeElement.value = ''; - dispatchEvent(input.nativeElement, 'input'); + var input = fixture.debugElement.query(By.css('input')); + input.nativeElement.value = ''; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.valid).toBe(false); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'num': null}); + expect(fixture.debugElement.componentInstance.form.valid).toBe(false); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'num': null + }); - input.nativeElement.value = '0'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = '0'; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.valid).toBe(true); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'num': 0}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.valid).toBe(true); + expect(fixture.debugElement.componentInstance.form.value).toEqual({'num': 0}); + async.done(); + }); })); @@ -432,16 +483,19 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.debugElement.componentInstance.data = null; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.debugElement.componentInstance.data = null; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual(''); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual(''); - async.done(); - }); + async.done(); + }); })); it('should support ', @@ -453,24 +507,27 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = new FormGroup({ - 'foodChicken': new FormControl(new RadioButtonState(false, 'chicken')), - 'foodFish': new FormControl(new RadioButtonState(true, 'fish')) - }); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = new FormGroup({ + 'foodChicken': new FormControl(new RadioButtonState(false, 'chicken')), + 'foodFish': new FormControl(new RadioButtonState(true, 'fish')) + }); + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.checked).toEqual(false); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.checked).toEqual(false); - dispatchEvent(input.nativeElement, 'change'); - fixture.detectChanges(); + dispatchEvent(input.nativeElement, 'change'); + fixture.detectChanges(); - let value = fixture.debugElement.componentInstance.form.value; - expect(value['foodChicken'].checked).toEqual(true); - expect(value['foodFish'].checked).toEqual(false); - async.done(); - }); + let value = fixture.debugElement.componentInstance.form.value; + expect(value['foodChicken'].checked).toEqual(true); + expect(value['foodFish'].checked).toEqual(false); + async.done(); + }); })); describe('should support `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var sfOption = fixture.debugElement.query(By.css('option')); + var select = fixture.debugElement.query(By.css('select')); + var sfOption = fixture.debugElement.query(By.css('option')); - expect(select.nativeElement.value).toEqual('SF'); - expect(sfOption.nativeElement.selected).toBe(true); - async.done(); - }); + expect(select.nativeElement.value).toEqual('SF'); + expect(sfOption.nativeElement.selected).toBe(true); + async.done(); + }); })); it('with basic selection and value bindings', @@ -505,19 +565,22 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - var testComp = fixture.debugElement.componentInstance; - testComp.list = [{'id': '0', 'name': 'SF'}, {'id': '1', 'name': 'NYC'}]; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + var testComp = fixture.debugElement.componentInstance; + testComp.list = [{'id': '0', 'name': 'SF'}, {'id': '1', 'name': 'NYC'}]; + fixture.detectChanges(); - var sfOption = fixture.debugElement.query(By.css('option')); - expect(sfOption.nativeElement.value).toEqual('0'); + var sfOption = fixture.debugElement.query(By.css('option')); + expect(sfOption.nativeElement.value).toEqual('0'); - testComp.list[0]['id'] = '2'; - fixture.detectChanges(); - expect(sfOption.nativeElement.value).toEqual('2'); - async.done(); - }); + testComp.list[0]['id'] = '2'; + fixture.detectChanges(); + expect(sfOption.nativeElement.value).toEqual('2'); + async.done(); + }); })); it('with ngControl', @@ -531,27 +594,30 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'city': new FormControl('SF')}); - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'city': new FormControl('SF')}); + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var sfOption = fixture.debugElement.query(By.css('option')); + var select = fixture.debugElement.query(By.css('select')); + var sfOption = fixture.debugElement.query(By.css('option')); - expect(select.nativeElement.value).toEqual('SF'); - expect(sfOption.nativeElement.selected).toBe(true); + expect(select.nativeElement.value).toEqual('SF'); + expect(sfOption.nativeElement.selected).toBe(true); - select.nativeElement.value = 'NYC'; - dispatchEvent(select.nativeElement, 'change'); + select.nativeElement.value = 'NYC'; + dispatchEvent(select.nativeElement, 'change'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({ - 'city': 'NYC' - }); - expect(sfOption.nativeElement.selected).toBe(false); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'city': 'NYC' + }); + expect(sfOption.nativeElement.selected).toBe(false); + async.done(); + }); })); it('with a dynamic list of options', @@ -589,27 +655,30 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; - testComp.selectedCity = testComp.list[1]; - fixture.detectChanges(); + var testComp = fixture.debugElement.componentInstance; + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}]; + testComp.selectedCity = testComp.list[1]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var nycOption = fixture.debugElement.queryAll(By.css('option'))[1]; + var select = fixture.debugElement.query(By.css('select')); + var nycOption = fixture.debugElement.queryAll(By.css('option'))[1]; - expect(select.nativeElement.value).toEqual('1: Object'); - expect(nycOption.nativeElement.selected).toBe(true); + expect(select.nativeElement.value).toEqual('1: Object'); + expect(nycOption.nativeElement.selected).toBe(true); - select.nativeElement.value = '2: Object'; - dispatchEvent(select.nativeElement, 'change'); - fixture.detectChanges(); - TimerWrapper.setTimeout(() => { - expect(testComp.selectedCity['name']).toEqual('Buffalo'); - async.done(); - }, 0); - }); + select.nativeElement.value = '2: Object'; + dispatchEvent(select.nativeElement, 'change'); + fixture.detectChanges(); + TimerWrapper.setTimeout(() => { + expect(testComp.selectedCity['name']).toEqual('Buffalo'); + async.done(); + }, 0); + }); })); it('when new options are added (selection through the model)', @@ -622,23 +691,26 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp: MyComp8 = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; - testComp.selectedCity = testComp.list[1]; - fixture.detectChanges(); + var testComp: MyComp8 = fixture.debugElement.componentInstance; + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; + testComp.selectedCity = testComp.list[1]; + fixture.detectChanges(); - testComp.list.push({'name': 'Buffalo'}); - testComp.selectedCity = testComp.list[2]; - fixture.detectChanges(); + testComp.list.push({'name': 'Buffalo'}); + testComp.selectedCity = testComp.list[2]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var buffalo = fixture.debugElement.queryAll(By.css('option'))[2]; - expect(select.nativeElement.value).toEqual('2: Object'); - expect(buffalo.nativeElement.selected).toBe(true); - async.done(); - }); + var select = fixture.debugElement.query(By.css('select')); + var buffalo = fixture.debugElement.queryAll(By.css('option'))[2]; + expect(select.nativeElement.value).toEqual('2: Object'); + expect(buffalo.nativeElement.selected).toBe(true); + async.done(); + }); })); it('when new options are added (selection through the UI)', @@ -651,25 +723,28 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp: MyComp8 = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; - testComp.selectedCity = testComp.list[0]; - fixture.detectChanges(); + var testComp: MyComp8 = fixture.debugElement.componentInstance; + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; + testComp.selectedCity = testComp.list[0]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var ny = fixture.debugElement.queryAll(By.css('option'))[1]; + var select = fixture.debugElement.query(By.css('select')); + var ny = fixture.debugElement.queryAll(By.css('option'))[1]; - select.nativeElement.value = '1: Object'; - dispatchEvent(select.nativeElement, 'change'); - testComp.list.push({'name': 'Buffalo'}); - fixture.detectChanges(); + select.nativeElement.value = '1: Object'; + dispatchEvent(select.nativeElement, 'change'); + testComp.list.push({'name': 'Buffalo'}); + fixture.detectChanges(); - expect(select.nativeElement.value).toEqual('1: Object'); - expect(ny.nativeElement.selected).toBe(true); - async.done(); - }); + expect(select.nativeElement.value).toEqual('1: Object'); + expect(ny.nativeElement.selected).toBe(true); + async.done(); + }); })); @@ -682,22 +757,25 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp: MyComp8 = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; - testComp.selectedCity = testComp.list[1]; - fixture.detectChanges(); + var testComp: MyComp8 = fixture.debugElement.componentInstance; + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; + testComp.selectedCity = testComp.list[1]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - expect(select.nativeElement.value).toEqual('1: Object'); + var select = fixture.debugElement.query(By.css('select')); + expect(select.nativeElement.value).toEqual('1: Object'); - testComp.list.pop(); - fixture.detectChanges(); + testComp.list.pop(); + fixture.detectChanges(); - expect(select.nativeElement.value).not.toEqual('1: Object'); - async.done(); - }); + expect(select.nativeElement.value).not.toEqual('1: Object'); + async.done(); + }); })); it('when option values change identity while tracking by index', @@ -710,25 +788,28 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp = fixture.debugElement.componentInstance; + var testComp = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; - testComp.selectedCity = testComp.list[0]; - fixture.detectChanges(); + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}]; + testComp.selectedCity = testComp.list[0]; + fixture.detectChanges(); - testComp.list[1] = 'Buffalo'; - testComp.selectedCity = testComp.list[1]; - fixture.detectChanges(); + testComp.list[1] = 'Buffalo'; + testComp.selectedCity = testComp.list[1]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var buffalo = fixture.debugElement.queryAll(By.css('option'))[1]; + var select = fixture.debugElement.query(By.css('select')); + var buffalo = fixture.debugElement.queryAll(By.css('option'))[1]; - expect(select.nativeElement.value).toEqual('1: Buffalo'); - expect(buffalo.nativeElement.selected).toBe(true); - async.done(); - }); + expect(select.nativeElement.value).toEqual('1: Buffalo'); + expect(buffalo.nativeElement.selected).toBe(true); + async.done(); + }); })); it('with duplicate option values', @@ -741,24 +822,27 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp = fixture.debugElement.componentInstance; + var testComp = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'NYC'}, {'name': 'SF'}, {'name': 'SF'}]; - testComp.selectedCity = testComp.list[0]; - fixture.detectChanges(); + testComp.list = [{'name': 'NYC'}, {'name': 'SF'}, {'name': 'SF'}]; + testComp.selectedCity = testComp.list[0]; + fixture.detectChanges(); - testComp.selectedCity = testComp.list[1]; - fixture.detectChanges(); + testComp.selectedCity = testComp.list[1]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var firstSF = fixture.debugElement.queryAll(By.css('option'))[1]; + var select = fixture.debugElement.query(By.css('select')); + var firstSF = fixture.debugElement.queryAll(By.css('option'))[1]; - expect(select.nativeElement.value).toEqual('1: Object'); - expect(firstSF.nativeElement.selected).toBe(true); - async.done(); - }); + expect(select.nativeElement.value).toEqual('1: Object'); + expect(firstSF.nativeElement.selected).toBe(true); + async.done(); + }); })); it('when option values have same content, but different identities', @@ -771,23 +855,26 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { - var testComp = fixture.debugElement.componentInstance; - testComp.list = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'NYC'}]; - testComp.selectedCity = testComp.list[0]; - fixture.detectChanges(); + var testComp = fixture.debugElement.componentInstance; + testComp.list = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'NYC'}]; + testComp.selectedCity = testComp.list[0]; + fixture.detectChanges(); - testComp.selectedCity = testComp.list[2]; - fixture.detectChanges(); + testComp.selectedCity = testComp.list[2]; + fixture.detectChanges(); - var select = fixture.debugElement.query(By.css('select')); - var secondNYC = fixture.debugElement.queryAll(By.css('option'))[2]; + var select = fixture.debugElement.query(By.css('select')); + var secondNYC = fixture.debugElement.queryAll(By.css('option'))[2]; - expect(select.nativeElement.value).toEqual('2: Object'); - expect(secondNYC.nativeElement.selected).toBe(true); - async.done(); - }); + expect(select.nativeElement.value).toEqual('2: Object'); + expect(secondNYC.nativeElement.selected).toBe(true); + async.done(); + }); })); }); @@ -799,19 +886,24 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'name': new FormControl('aa')}); - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('!aa!'); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'name': new FormControl('aa')}); + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('!aa!'); - input.nativeElement.value = '!bb!'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = '!bb!'; + dispatchEvent(input.nativeElement, 'input'); - expect(fixture.debugElement.componentInstance.form.value).toEqual({'name': 'bb'}); - async.done(); - }); + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'name': 'bb' + }); + async.done(); + }); })); it('should support custom value accessors on non builtin input elements that fire a change event without a \'target\' property', @@ -822,22 +914,25 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = - new FormGroup({'name': new FormControl('aa')}); - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('my-input')); - expect(input.componentInstance.value).toEqual('!aa!'); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = + new FormGroup({'name': new FormControl('aa')}); + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('my-input')); + expect(input.componentInstance.value).toEqual('!aa!'); - input.componentInstance.value = '!bb!'; - ObservableWrapper.subscribe(input.componentInstance.onInput, (value) => { - expect(fixture.debugElement.componentInstance.form.value).toEqual({ - 'name': 'bb' + input.componentInstance.value = '!bb!'; + ObservableWrapper.subscribe(input.componentInstance.onInput, (value) => { + expect(fixture.debugElement.componentInstance.form.value).toEqual({ + 'name': 'bb' + }); + async.done(); + }); + input.componentInstance.dispatchChangeEvent(); }); - async.done(); - }); - input.componentInstance.dispatchChangeEvent(); - }); })); }); @@ -859,38 +954,41 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - var required = fixture.debugElement.query(By.css('[required]')); - var minLength = fixture.debugElement.query(By.css('[minlength]')); - var maxLength = fixture.debugElement.query(By.css('[maxlength]')); + var required = fixture.debugElement.query(By.css('[required]')); + var minLength = fixture.debugElement.query(By.css('[minlength]')); + var maxLength = fixture.debugElement.query(By.css('[maxlength]')); - required.nativeElement.value = ''; - minLength.nativeElement.value = '1'; - maxLength.nativeElement.value = '1234'; - dispatchEvent(required.nativeElement, 'input'); - dispatchEvent(minLength.nativeElement, 'input'); - dispatchEvent(maxLength.nativeElement, 'input'); + required.nativeElement.value = ''; + minLength.nativeElement.value = '1'; + maxLength.nativeElement.value = '1234'; + dispatchEvent(required.nativeElement, 'input'); + dispatchEvent(minLength.nativeElement, 'input'); + dispatchEvent(maxLength.nativeElement, 'input'); - expect(form.hasError('required', ['login'])).toEqual(true); - expect(form.hasError('minlength', ['min'])).toEqual(true); - expect(form.hasError('maxlength', ['max'])).toEqual(true); + expect(form.hasError('required', ['login'])).toEqual(true); + expect(form.hasError('minlength', ['min'])).toEqual(true); + expect(form.hasError('maxlength', ['max'])).toEqual(true); - expect(form.hasError('loginIsEmpty')).toEqual(true); + expect(form.hasError('loginIsEmpty')).toEqual(true); - required.nativeElement.value = '1'; - minLength.nativeElement.value = '123'; - maxLength.nativeElement.value = '123'; - dispatchEvent(required.nativeElement, 'input'); - dispatchEvent(minLength.nativeElement, 'input'); - dispatchEvent(maxLength.nativeElement, 'input'); + required.nativeElement.value = '1'; + minLength.nativeElement.value = '123'; + maxLength.nativeElement.value = '123'; + dispatchEvent(required.nativeElement, 'input'); + dispatchEvent(minLength.nativeElement, 'input'); + dispatchEvent(maxLength.nativeElement, 'input'); - expect(form.valid).toEqual(true); + expect(form.valid).toEqual(true); - async.done(); - }); + async.done(); + }); })); it('should use async validators defined in the html', @@ -932,19 +1030,22 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); - expect(form.valid).toEqual(true); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); + expect(form.valid).toEqual(true); - var input = fixture.debugElement.query(By.css('input')); + var input = fixture.debugElement.query(By.css('input')); - input.nativeElement.value = ''; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = ''; + dispatchEvent(input.nativeElement, 'input'); - expect(form.valid).toEqual(false); - async.done(); - }); + expect(form.valid).toEqual(false); + async.done(); + }); })); it('should use async validators defined in the model', @@ -997,14 +1098,17 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); - expect(input.nativeElement.value).toEqual('value'); - async.done(); - }); + var input = fixture.debugElement.query(By.css('input')); + expect(input.nativeElement.value).toEqual('value'); + async.done(); + }); })); it('should update the control group values on DOM change', @@ -1020,17 +1124,20 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); + var input = fixture.debugElement.query(By.css('input')); - input.nativeElement.value = 'updatedValue'; - dispatchEvent(input.nativeElement, 'input'); + input.nativeElement.value = 'updatedValue'; + dispatchEvent(input.nativeElement, 'input'); - expect(form.value).toEqual({'nested': {'login': 'updatedValue'}}); - async.done(); - }); + expect(form.value).toEqual({'nested': {'login': 'updatedValue'}}); + async.done(); + }); })); }); @@ -1125,13 +1232,16 @@ export function main() { const t = `
`; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.name = null; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.name = null; + fixture.detectChanges(); - expect(fixture.debugElement.children[0].providerTokens.length).toEqual(0); - async.done(); - }); + expect(fixture.debugElement.children[0].providerTokens.length).toEqual(0); + async.done(); + }); })); it('should remove controls', @@ -1255,11 +1365,14 @@ export function main() { `; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - expect(() => fixture.detectChanges()) - .toThrowError(new RegExp(`Name attribute must be set`)); - async.done(); - }); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + expect(() => fixture.detectChanges()) + .toThrowError(new RegExp(`Name attribute must be set`)); + async.done(); + }); })); it('should override name attribute with ngModelOptions name if provided', @@ -1361,29 +1474,32 @@ export function main() { const t = `
`; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')).nativeElement; - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-untouched' - ]); + var input = fixture.debugElement.query(By.css('input')).nativeElement; + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-untouched' + ]); - dispatchEvent(input, 'blur'); - fixture.detectChanges(); + dispatchEvent(input, 'blur'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-touched' - ]); + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-touched' + ]); - input.value = 'updatedValue'; - dispatchEvent(input, 'input'); - fixture.detectChanges(); + input.value = 'updatedValue'; + dispatchEvent(input, 'input'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); - async.done(); - }); + expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); + async.done(); + }); })); it('should work with complex model-driven forms', @@ -1395,29 +1511,32 @@ export function main() { const t = `
`; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.form = form; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.form = form; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')).nativeElement; - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-untouched' - ]); + var input = fixture.debugElement.query(By.css('input')).nativeElement; + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-untouched' + ]); - dispatchEvent(input, 'blur'); - fixture.detectChanges(); + dispatchEvent(input, 'blur'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-touched' - ]); + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-touched' + ]); - input.value = 'updatedValue'; - dispatchEvent(input, 'input'); - fixture.detectChanges(); + input.value = 'updatedValue'; + dispatchEvent(input, 'input'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); - async.done(); - }); + expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); + async.done(); + }); })); it('should work with ngModel', @@ -1426,29 +1545,32 @@ export function main() { (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { const t = `
`; - tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => { - fixture.debugElement.componentInstance.name = ''; - fixture.detectChanges(); + tcb.overrideTemplate(MyComp8, t) + .overrideProviders(MyComp8, providerArr) + .createAsync(MyComp8) + .then((fixture) => { + fixture.debugElement.componentInstance.name = ''; + fixture.detectChanges(); - var input = fixture.debugElement.query(By.css('input')).nativeElement; - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-untouched' - ]); + var input = fixture.debugElement.query(By.css('input')).nativeElement; + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-untouched' + ]); - dispatchEvent(input, 'blur'); - fixture.detectChanges(); + dispatchEvent(input, 'blur'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual([ - 'ng-invalid', 'ng-pristine', 'ng-touched' - ]); + expect(sortedClassList(input)).toEqual([ + 'ng-invalid', 'ng-pristine', 'ng-touched' + ]); - input.value = 'updatedValue'; - dispatchEvent(input, 'input'); - fixture.detectChanges(); + input.value = 'updatedValue'; + dispatchEvent(input, 'input'); + fixture.detectChanges(); - expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); - async.done(); - }); + expect(sortedClassList(input)).toEqual(['ng-dirty', 'ng-touched', 'ng-valid']); + async.done(); + }); })); }); diff --git a/modules/@angular/common/test/forms/model_spec.ts b/modules/@angular/forms/test/model_spec.ts similarity index 99% rename from modules/@angular/common/test/forms/model_spec.ts rename to modules/@angular/forms/test/model_spec.ts index e645b2c428..295189c438 100644 --- a/modules/@angular/common/test/forms/model_spec.ts +++ b/modules/@angular/forms/test/model_spec.ts @@ -1,10 +1,10 @@ import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach, inject,} from '@angular/core/testing/testing_internal'; import {fakeAsync, flushMicrotasks, Log, tick} from '@angular/core/testing'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; -import {FormGroup, FormControl, FormArray, Validators} from '@angular/common/src/forms'; -import {IS_DART, isPresent} from '../../src/facade/lang'; -import {PromiseWrapper} from '../../src/facade/promise'; -import {TimerWrapper, ObservableWrapper, EventEmitter} from '../../src/facade/async'; +import {FormGroup, FormControl, FormArray, Validators} from '@angular/forms'; +import {IS_DART, isPresent} from '../src/facade/lang'; +import {PromiseWrapper} from '../src/facade/promise'; +import {TimerWrapper, ObservableWrapper, EventEmitter} from '../src/facade/async'; export function main() { function asyncValidator(expected: any /** TODO #9100 */, timeouts = /*@ts2dart_const*/ {}) { diff --git a/modules/@angular/forms/test/spies.dart b/modules/@angular/forms/test/spies.dart new file mode 100644 index 0000000000..6c94ca2246 --- /dev/null +++ b/modules/@angular/forms/test/spies.dart @@ -0,0 +1,14 @@ +library core.spies; + +import 'package:angular2/common.dart'; +import 'package:angular2/src/core/change_detection/change_detection.dart'; +import 'package:angular2/testing_internal.dart'; + +@proxy +class SpyNgControl extends SpyObject implements NgControl {} + +@proxy +class SpyValueAccessor extends SpyObject implements ControlValueAccessor {} + +@proxy +class SpyChangeDetectorRef extends SpyObject implements ChangeDetectorRef {} diff --git a/modules/@angular/forms/test/spies.ts b/modules/@angular/forms/test/spies.ts new file mode 100644 index 0000000000..bfd86a6610 --- /dev/null +++ b/modules/@angular/forms/test/spies.ts @@ -0,0 +1,13 @@ +import {ChangeDetectorRef} from '@angular/core/src/change_detection/change_detector_ref'; +import {SpyObject, proxy} from '@angular/core/testing/testing_internal'; + +export class SpyChangeDetectorRef extends SpyObject { + constructor() { + super(ChangeDetectorRef); + this.spy('markForCheck'); + } +} + +export class SpyNgControl extends SpyObject {} + +export class SpyValueAccessor extends SpyObject { writeValue: any; } diff --git a/modules/@angular/common/test/forms/validators_spec.ts b/modules/@angular/forms/test/validators_spec.ts similarity index 98% rename from modules/@angular/common/test/forms/validators_spec.ts rename to modules/@angular/forms/test/validators_spec.ts index 887d060815..17002dc4d0 100644 --- a/modules/@angular/common/test/forms/validators_spec.ts +++ b/modules/@angular/forms/test/validators_spec.ts @@ -1,9 +1,9 @@ -import {AbstractControl, FormControl, Validators} from '@angular/common/src/forms'; import {Log, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; import {afterEach, beforeEach, ddescribe, describe, expect, iit, it, xit} from '@angular/core/testing/testing_internal'; +import {AbstractControl, FormControl, Validators} from '@angular/forms'; -import {EventEmitter, ObservableWrapper, TimerWrapper} from '../../src/facade/async'; -import {PromiseWrapper} from '../../src/facade/promise'; +import {EventEmitter, ObservableWrapper, TimerWrapper} from '../src/facade/async'; +import {PromiseWrapper} from '../src/facade/promise'; export function main() { function validator(key: string, error: any) { diff --git a/modules/@angular/forms/tsconfig-es2015.json b/modules/@angular/forms/tsconfig-es2015.json new file mode 100644 index 0000000000..788a51db95 --- /dev/null +++ b/modules/@angular/forms/tsconfig-es2015.json @@ -0,0 +1,31 @@ +{ + "angularCompilerOptions": { + "skipTemplateCodegen": true + }, + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "stripInternal": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "es2015", + "moduleResolution": "node", + "outDir": "../../../dist/packages-dist/forms/esm", + "paths": { + "@angular/core": ["../../../dist/packages-dist/core"], + "@angular/core/testing": ["../../../dist/packages-dist/core/testing"], + "@angular/common": ["../../../dist/packages-dist/common"], + "@angular/common/testing": ["../../../dist/packages-dist/common/testing"], + "@angular/compiler": ["../../../dist/packages-dist/compiler"], + "@angular/compiler/testing": ["../../../dist/packages-dist/compiler/testing"] + }, + "rootDir": ".", + "sourceMap": true, + "inlineSources": true, + "target": "es2015" + }, + "files": [ + "index.ts", + "../../../node_modules/zone.js/dist/zone.js.d.ts" + ] +} diff --git a/modules/@angular/forms/tsconfig-es5.json b/modules/@angular/forms/tsconfig-es5.json new file mode 100644 index 0000000000..2fd7aa1799 --- /dev/null +++ b/modules/@angular/forms/tsconfig-es5.json @@ -0,0 +1,32 @@ +{ + "angularCompilerOptions": { + "skipTemplateCodegen": true + }, + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "stripInternal": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "commonjs", + "moduleResolution": "node", + "outDir": "../../../dist/packages-dist/forms/", + "paths": { + "@angular/core": ["../../../dist/packages-dist/core/"], + "@angular/core/testing": ["../../../dist/packages-dist/core/testing"], + "@angular/common": ["../../../dist/packages-dist/common"], + "@angular/common/testing": ["../../../dist/packages-dist/common/testing"], + "@angular/compiler": ["../../../dist/packages-dist/compiler"], + "@angular/compiler/testing": ["../../../dist/packages-dist/compiler/testing"] + }, + "rootDir": ".", + "sourceMap": true, + "inlineSources": true, + "lib": ["es6", "dom"], + "target": "es5" + }, + "files": [ + "index.ts", + "../../../node_modules/zone.js/dist/zone.js.d.ts" + ] +} diff --git a/modules/@angular/platform-browser-dynamic/index.ts b/modules/@angular/platform-browser-dynamic/index.ts index bfacad444e..66c8b85072 100644 --- a/modules/@angular/platform-browser-dynamic/index.ts +++ b/modules/@angular/platform-browser-dynamic/index.ts @@ -1,6 +1,6 @@ import {COMMON_DIRECTIVES, COMMON_PIPES} from '@angular/common'; import {COMPILER_PROVIDERS, CompilerConfig, XHR} from '@angular/compiler'; -import {ApplicationRef, ComponentRef, ReflectiveInjector, Type, coreLoadAndBootstrap} from '@angular/core'; +import {ApplicationRef, ComponentRef, PLATFORM_DIRECTIVES, PLATFORM_PIPES, ReflectiveInjector, Type, coreLoadAndBootstrap} from '@angular/core'; import {BROWSER_APP_PROVIDERS, WORKER_APP_APPLICATION_PROVIDERS, WORKER_RENDER_APPLICATION_PROVIDERS, WORKER_SCRIPT, browserPlatform, workerAppPlatform, workerRenderPlatform} from '@angular/platform-browser'; import {ReflectionCapabilities, reflector} from './core_private'; @@ -11,13 +11,16 @@ import {XHRImpl} from './src/xhr/xhr_impl'; export const BROWSER_APP_COMPILER_PROVIDERS: Array = [ - COMPILER_PROVIDERS, - { + COMPILER_PROVIDERS, { provide: CompilerConfig, - useValue: - new CompilerConfig({platformDirectives: COMMON_DIRECTIVES, platformPipes: COMMON_PIPES}) + useFactory: (platformDirectives: any[], platformPipes: any[]) => { + return new CompilerConfig({platformDirectives, platformPipes}); + }, + deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] }, {provide: XHR, useClass: XHRImpl}, + {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true}, + {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true} ]; @@ -125,13 +128,16 @@ export function bootstrapRender( const WORKER_APP_COMPILER_PROVIDERS: Array = [ - COMPILER_PROVIDERS, - { + COMPILER_PROVIDERS, { provide: CompilerConfig, - useValue: - new CompilerConfig({platformDirectives: COMMON_DIRECTIVES, platformPipes: COMMON_PIPES}) + useFactory: (platformDirectives: any[], platformPipes: any[]) => { + return new CompilerConfig({platformDirectives, platformPipes}); + }, + deps: [PLATFORM_DIRECTIVES, PLATFORM_PIPES] }, {provide: XHR, useClass: XHRImpl}, + {provide: PLATFORM_DIRECTIVES, useValue: COMMON_DIRECTIVES, multi: true}, + {provide: PLATFORM_PIPES, useValue: COMMON_PIPES, multi: true} ]; diff --git a/modules/@angular/platform-browser/src/browser.ts b/modules/@angular/platform-browser/src/browser.ts index acc6120c9d..af0429bbec 100644 --- a/modules/@angular/platform-browser/src/browser.ts +++ b/modules/@angular/platform-browser/src/browser.ts @@ -58,7 +58,6 @@ export const BROWSER_APP_PROVIDERS: Array = [ Testability, EventManager, ELEMENT_PROBE_PROVIDERS ]; - export function browserPlatform(): PlatformRef { if (isBlank(getPlatform())) { createPlatform(ReflectiveInjector.resolveAndCreate(BROWSER_PLATFORM_PROVIDERS)); diff --git a/test-main.js b/test-main.js index f4e23eef28..644375ef08 100644 --- a/test-main.js +++ b/test-main.js @@ -32,6 +32,10 @@ System.config({ main: 'index.js', defaultExtension: 'js' }, + '@angular/forms': { + main: 'index.js', + defaultExtension: 'js' + }, // remove after all tests imports are fixed '@angular/facade': { main: 'index.js', diff --git a/tools/cjs-jasmine/index.ts b/tools/cjs-jasmine/index.ts index d8c744cd45..ba5193597d 100644 --- a/tools/cjs-jasmine/index.ts +++ b/tools/cjs-jasmine/index.ts @@ -42,7 +42,7 @@ var specFiles: any = '@angular/platform-browser-dynamic/**', '@angular/core/test/zone/**', '@angular/core/test/fake_async_spec.*', - '@angular/common/test/forms/**', + '@angular/forms/test/**', '@angular/common/test/forms-deprecated/**', '@angular/router/test/route_config/route_config_spec.*', '@angular/router/test/integration/bootstrap_spec.*', diff --git a/tools/public_api_guard/public_api_spec.ts b/tools/public_api_guard/public_api_spec.ts index c142c9c8e6..f7fbe85038 100644 --- a/tools/public_api_guard/public_api_spec.ts +++ b/tools/public_api_guard/public_api_spec.ts @@ -169,7 +169,9 @@ const CORE = [ 'const AUTO_STYLE:any', 'const PACKAGE_ROOT_URL:any', 'const PLATFORM_COMMON_PROVIDERS:Array', + 'const PLATFORM_DIRECTIVES:OpaqueToken', 'const PLATFORM_INITIALIZER:any', + 'const PLATFORM_PIPES:OpaqueToken', 'ContentChildMetadata', 'ContentChildMetadata.constructor(_selector:Type|string, {read=null}:{read?:any}={})', 'ContentChildMetadataFactory',