Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -177,7 +177,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 @@ -192,7 +192,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 @@ -232,6 +231,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
112 changes: 81 additions & 31 deletions test/directive/ng_model_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,37 +51,6 @@ describe('ng-model', () {
expect(_.rootScope.model).toEqual('def');
}));

it('should update model from the input value for type=number', 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 = '12';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual('12');

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

it('should update input type=number to blank when model is null', 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 = '12';
_.triggerEvent(inputElement, 'change');
expect(_.rootScope.model).toEqual('12');

_.rootScope.model = null;
_.rootScope.$apply();
expect(inputElement.value).toEqual('');
}));

it('should write to input only if value is different', inject(() {
var scope = _.rootScope;
var element = new dom.InputElement();
Expand Down Expand Up @@ -109,6 +78,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