Skip to content

Commit 65c737f

Browse files
committed
feat(forms): add input[type=number] value accessor
Closes angular#4014 Closes angular#4761
1 parent 427860a commit 65c737f

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

modules/angular2/src/core/forms/directives.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {NgFormModel} from './directives/ng_form_model';
77
import {NgForm} from './directives/ng_form';
88
import {DefaultValueAccessor} from './directives/default_value_accessor';
99
import {CheckboxControlValueAccessor} from './directives/checkbox_value_accessor';
10+
import {NumberValueAccessor} from './directives/number_value_accessor';
1011
import {NgControlStatus} from './directives/ng_control_status';
1112
import {
1213
SelectControlValueAccessor,
@@ -58,6 +59,7 @@ export const FORM_DIRECTIVES: Type[] = CONST_EXPR([
5859

5960
NgSelectOption,
6061
DefaultValueAccessor,
62+
NumberValueAccessor,
6163
CheckboxControlValueAccessor,
6264
SelectControlValueAccessor,
6365
NgControlStatus,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {Directive} from 'angular2/src/core/metadata';
2+
import {ElementRef} from 'angular2/src/core/linker';
3+
import {Renderer} from 'angular2/src/core/render';
4+
import {Self, forwardRef, Provider} from 'angular2/src/core/di';
5+
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from './control_value_accessor';
6+
import {isBlank, CONST_EXPR, NumberWrapper} from 'angular2/src/core/facade/lang';
7+
import {setProperty} from './shared';
8+
9+
const NUMBER_VALUE_ACCESSOR = CONST_EXPR(new Provider(
10+
NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => NumberValueAccessor), multi: true}));
11+
12+
/**
13+
* The accessor for writing a number value and listening to changes that is used by the
14+
* {@link NgModel}, {@link NgFormControl}, and {@link NgControlName} directives.
15+
*
16+
* # Example
17+
* ```
18+
* <input type="number" [(ng-model)]="age">
19+
* ```
20+
*/
21+
@Directive({
22+
selector:
23+
'input[type=number][ng-control],input[type=number][ng-form-control],input[type=number][ng-model]',
24+
host: {
25+
'(change)': 'onChange($event.target.value)',
26+
'(input)': 'onChange($event.target.value)',
27+
'(blur)': 'onTouched()'
28+
},
29+
bindings: [NUMBER_VALUE_ACCESSOR]
30+
})
31+
export class NumberValueAccessor implements ControlValueAccessor {
32+
onChange = (_) => {};
33+
onTouched = () => {};
34+
35+
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
36+
37+
writeValue(value: number): void { setProperty(this._renderer, this._elementRef, 'value', value); }
38+
39+
registerOnChange(fn: (_: number) => void): void {
40+
this.onChange = (value) => { fn(NumberWrapper.parseFloat(value)); };
41+
}
42+
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
43+
}

modules/angular2/src/core/forms/directives/shared.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {ControlValueAccessor} from './control_value_accessor';
1010
import {ElementRef, QueryList} from 'angular2/src/core/linker';
1111
import {Renderer} from 'angular2/src/core/render';
1212
import {DefaultValueAccessor} from './default_value_accessor';
13+
import {NumberValueAccessor} from './number_value_accessor';
1314
import {CheckboxControlValueAccessor} from './checkbox_value_accessor';
1415
import {SelectControlValueAccessor} from './select_control_value_accessor';
1516

@@ -72,7 +73,7 @@ export function selectValueAccessor(dir: NgControl, valueAccessors: ControlValue
7273
if (v instanceof DefaultValueAccessor) {
7374
defaultAccessor = v;
7475

75-
} else if (v instanceof CheckboxControlValueAccessor ||
76+
} else if (v instanceof CheckboxControlValueAccessor || v instanceof NumberValueAccessor ||
7677
v instanceof SelectControlValueAccessor) {
7778
if (isPresent(builtinAccessor))
7879
_throwError(dir, "More than one built-in value accessor matches");

modules/angular2/test/core/forms/integration_spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,28 @@ export function main() {
274274
});
275275
}));
276276

277+
it("should support <type=number>",
278+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
279+
var t = `<div [ng-form-model]="form">
280+
<input type="number" ng-control="num">
281+
</div>`;
282+
283+
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => {
284+
rootTC.debugElement.componentInstance.form =
285+
new ControlGroup({"num": new Control(10)});
286+
rootTC.detectChanges();
287+
288+
var input = rootTC.debugElement.query(By.css("input"));
289+
expect(input.nativeElement.value).toEqual("10");
290+
291+
input.nativeElement.value = "20";
292+
dispatchEvent(input.nativeElement, "change");
293+
294+
expect(rootTC.debugElement.componentInstance.form.value).toEqual({"num": 20});
295+
async.done();
296+
});
297+
}));
298+
277299
it("should support <select>",
278300
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
279301
var t = `<div [ng-form-model]="form">

0 commit comments

Comments
 (0)