From 65c737fc951c0964e851ffaaee93f85278518491 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Thu, 15 Oct 2015 11:38:34 -0700 Subject: [PATCH] feat(forms): add input[type=number] value accessor Closes #4014 Closes #4761 --- modules/angular2/src/core/forms/directives.ts | 2 + .../forms/directives/number_value_accessor.ts | 43 +++++++++++++++++++ .../src/core/forms/directives/shared.ts | 3 +- .../test/core/forms/integration_spec.ts | 22 ++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 modules/angular2/src/core/forms/directives/number_value_accessor.ts diff --git a/modules/angular2/src/core/forms/directives.ts b/modules/angular2/src/core/forms/directives.ts index d1507894d2..d8c9f1ee9e 100644 --- a/modules/angular2/src/core/forms/directives.ts +++ b/modules/angular2/src/core/forms/directives.ts @@ -7,6 +7,7 @@ import {NgFormModel} from './directives/ng_form_model'; import {NgForm} from './directives/ng_form'; import {DefaultValueAccessor} from './directives/default_value_accessor'; import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor'; +import {NumberValueAccessor} from './directives/number_value_accessor'; import {NgControlStatus} from './directives/ng_control_status'; import { SelectControlValueAccessor, @@ -58,6 +59,7 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([ NgSelectOption, DefaultValueAccessor, + NumberValueAccessor, CheckboxControlValueAccessor, SelectControlValueAccessor, NgControlStatus, diff --git a/modules/angular2/src/core/forms/directives/number_value_accessor.ts b/modules/angular2/src/core/forms/directives/number_value_accessor.ts new file mode 100644 index 0000000000..d308db4ac3 --- /dev/null +++ b/modules/angular2/src/core/forms/directives/number_value_accessor.ts @@ -0,0 +1,43 @@ +import {Directive} from 'angular2/src/core/metadata'; +import {ElementRef} from 'angular2/src/core/linker'; +import {Renderer} from 'angular2/src/core/render'; +import {Self, forwardRef, Provider} from 'angular2/src/core/di'; +import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor'; +import {isBlank, CONST_EXPR, NumberWrapper} from 'angular2/src/core/facade/lang'; +import {setProperty} from './shared'; + +const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider( + NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => NumberValueAccessor), multi: true})); + +/** + * The accessor for writing a number value and listening to changes that is used by the + * {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives. + * + * # Example + * ``` + * + * ``` + */ +@Directive({ + selector: + 'input[type=number][ng-control],input[type=number][ng-form-control],input[type=number][ng-model]', + host: { + '(change)': 'onChange($event.target.value)', + '(input)': 'onChange($event.target.value)', + '(blur)': 'onTouched()' + }, + bindings: [NUMBER_VALUE_ACCESSOR] +}) +export class NumberValueAccessor implements ControlValueAccessor { + onChange = (_) => {}; + onTouched = () => {}; + + constructor(private _renderer: Renderer, private _elementRef: ElementRef) {} + + writeValue(value: number): void { setProperty(this._renderer, this._elementRef, 'value', value); } + + registerOnChange(fn: (_: number) => void): void { + this.onChange = (value) => { fn(NumberWrapper.parseFloat(value)); }; + } + registerOnTouched(fn: () => void): void { this.onTouched = fn; } +} diff --git a/modules/angular2/src/core/forms/directives/shared.ts b/modules/angular2/src/core/forms/directives/shared.ts index 7d577e7bfe..127dea54d6 100644 --- a/modules/angular2/src/core/forms/directives/shared.ts +++ b/modules/angular2/src/core/forms/directives/shared.ts @@ -10,6 +10,7 @@ import {ControlValueAccessor} from './control_value_accessor'; import {ElementRef, QueryList} from 'angular2/src/core/linker'; import {Renderer} from 'angular2/src/core/render'; import {DefaultValueAccessor} from './default_value_accessor'; +import {NumberValueAccessor} from './number_value_accessor'; import {CheckboxControlValueAccessor} from './checkbox_value_accessor'; import {SelectControlValueAccessor} from './select_control_value_accessor'; @@ -72,7 +73,7 @@ export function selectValueAccessor(dir: NgControl, valueAccessors: ControlValue if (v instanceof DefaultValueAccessor) { defaultAccessor = v; - } else if (v instanceof CheckboxControlValueAccessor || + } else if (v instanceof CheckboxControlValueAccessor || v instanceof NumberValueAccessor || v instanceof SelectControlValueAccessor) { if (isPresent(builtinAccessor)) _throwError(dir, "More than one built-in value accessor matches"); diff --git a/modules/angular2/test/core/forms/integration_spec.ts b/modules/angular2/test/core/forms/integration_spec.ts index d8aa08787a..61bcfba953 100644 --- a/modules/angular2/test/core/forms/integration_spec.ts +++ b/modules/angular2/test/core/forms/integration_spec.ts @@ -274,6 +274,28 @@ export function main() { }); })); + it("should support ", + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + var t = `
+ +
`; + + tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => { + rootTC.debugElement.componentInstance.form = + new ControlGroup({"num": new Control(10)}); + rootTC.detectChanges(); + + var input = rootTC.debugElement.query(By.css("input")); + expect(input.nativeElement.value).toEqual("10"); + + input.nativeElement.value = "20"; + dispatchEvent(input.nativeElement, "change"); + + expect(rootTC.debugElement.componentInstance.form.value).toEqual({"num": 20}); + async.done(); + }); + })); + it("should support