Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat(ngModel): Treat the values of number and range inputs as numbers
Browse files Browse the repository at this point in the history
Add a InputNumberLikeDirective that binds the model as a numeric
value, so that it can be bound to num types on scopes. Select the
directive for number and range input types.

Closes #527
  • Loading branch information
teropa authored and mhevery committed Feb 12, 2014
1 parent 06d6f28 commit e703bd1
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 6 deletions.
1 change: 1 addition & 0 deletions lib/directive/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class NgDirectiveModule extends Module {
value(NgShalowRepeatDirective, null);
value(NgShowDirective, null);
value(InputTextLikeDirective, null);
value(InputNumberLikeDirective, null);
value(InputRadioDirective, null);
value(InputCheckboxDirective, null);
value(InputSelectDirective, null);
Expand Down
41 changes: 39 additions & 2 deletions lib/directive/ng_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class InputCheckboxDirective {
/**
* Usage:
*
* <input type="text|number|url|password|email" ng-model="myModel">
* <input type="text|url|password|email" ng-model="myModel">
* <textarea ng-model="myModel"></textarea>
*
* This creates a two-way binding between any string-based input element
Expand All @@ -145,7 +145,6 @@ class InputCheckboxDirective {
@NgDirective(selector: 'input[type=password][ng-model]')
@NgDirective(selector: 'input[type=url][ng-model]')
@NgDirective(selector: 'input[type=email][ng-model]')
@NgDirective(selector: 'input[type=number][ng-model]')
@NgDirective(selector: 'input[type=search][ng-model]')
class InputTextLikeDirective {
final dom.Element inputElement;
Expand Down Expand Up @@ -185,6 +184,44 @@ class InputTextLikeDirective {
}
}

/**
* Usage:
*
* <input type="number|range" ng-model="myModel">
*
* This creates a two-way binding between a number-based input element
* so long as the ng-model attribute is present on the input element. Whenever
* the value of the input element changes then the matching model property on the
* scope will be updated as well as the other way around (when the scope property
* is updated).
*
*/
@NgDirective(selector: 'input[type=number][ng-model]')
@NgDirective(selector: 'input[type=range][ng-model]')
class InputNumberLikeDirective {
final dom.InputElement inputElement;
final NgModel ngModel;
final Scope scope;

InputNumberLikeDirective(dom.Element this.inputElement, this.ngModel, this.scope) {
ngModel.render = (value) {
inputElement.value = value == null ? '' : value.toString();
};
inputElement
..onChange.listen(relaxFnArgs(processValue))
..onInput.listen(relaxFnArgs(processValue));
}

processValue() {
var value = num.parse(inputElement.value, (_) => null);
if (value != ngModel.viewValue) {
ngModel.dirty = true;
scope.$apply(() => ngModel.viewValue = value);
}
ngModel.validate();
}
}

class _UidCounter {
static final int CHAR_0 = "0".codeUnitAt(0);
static final int CHAR_9 = "9".codeUnitAt(0);
Expand Down
89 changes: 85 additions & 4 deletions test/directive/ng_model_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ describe('ng-model', () {

inputElement.value = '12';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual('12');
expect(_.rootScope.model).toEqual(12);

inputElement.value = '14';
var input = probe.directive(InputTextLikeDirective);
var input = probe.directive(InputNumberLikeDirective);
input.processValue();
expect(_.rootScope.model).toEqual('14');
expect(_.rootScope.model).toEqual(14);
}));

it('should update input type=number to blank when model is null', inject(() {
Expand All @@ -75,7 +75,7 @@ describe('ng-model', () {

inputElement.value = '12';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual('12');
expect(_.rootScope.model).toEqual(12);

_.rootScope.model = null;
_.rootScope.$apply();
Expand Down Expand Up @@ -109,6 +109,87 @@ describe('ng-model', () {
}));
});

describe('type="number" like', () {
it('should update input value from model', inject(() {
_.compile('<input type="number" ng-model="model">');
_.rootScope.$digest();

_.rootScope.$apply('model = 42');
expect((_.rootElement as dom.InputElement).value).toEqual('42');
}));

it('should update input value from model for range inputs', inject(() {
_.compile('<input type="range" ng-model="model">');
_.rootScope.$digest();

_.rootScope.$apply('model = 42');
expect((_.rootElement as dom.InputElement).value).toEqual('42');
}));

it('should update model from the input value', inject(() {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.p;
var ngModel = probe.directive(NgModel);
InputElement inputElement = probe.element;

inputElement.value = '42';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual(42);

inputElement.value = '43';
var input = probe.directive(InputNumberLikeDirective);
input.processValue();
expect(_.rootScope.model).toEqual(43);
}));

it('should update model to null from a blank input value', inject(() {
_.compile('<input type="number" ng-model="model" probe="p">');
Probe probe = _.rootScope.p;
var ngModel = probe.directive(NgModel);
InputElement inputElement = probe.element;

inputElement.value = '';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toBeNull();
}));

it('should update model from the input value for range inputs', inject(() {
_.compile('<input type="range" ng-model="model" probe="p">');
Probe probe = _.rootScope.p;
var ngModel = probe.directive(NgModel);
InputElement inputElement = probe.element;

inputElement.value = '42';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual(42);

inputElement.value = '43';
var input = probe.directive(InputNumberLikeDirective);
input.processValue();
expect(_.rootScope.model).toEqual(43);
}));

it('should update model to a native default value from a blank range input value', inject(() {
_.compile('<input type="range" ng-model="model" probe="p">');
Probe probe = _.rootScope.p;
var ngModel = probe.directive(NgModel);
InputElement inputElement = probe.element;

inputElement.value = '';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toBeDefined();
}));

it('should render null as blank', inject(() {
_.compile('<input type="number" ng-model="model">');
_.rootScope.$digest();

_.rootScope.$apply('model = null');
expect((_.rootElement as dom.InputElement).value).toEqual('');
}));

});

describe('type="password"', () {
it('should update input value from model', inject(() {
_.compile('<input type="password" ng-model="model">');
Expand Down

0 comments on commit e703bd1

Please sign in to comment.