Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat(ngModel): allowinvalid #9003

Merged
merged 1 commit into from
Sep 9, 2014
Merged
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
17 changes: 15 additions & 2 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -2037,12 +2037,23 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
ctrl.$modelValue = ngModelGet();
}
var prevModelValue = ctrl.$modelValue;
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
if (allowInvalid) {
ctrl.$modelValue = modelValue;
writeToModelIfNeeded();
}
ctrl.$$runValidators(parserValid, modelValue, viewValue, function() {
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
if (!allowInvalid) {
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
writeToModelIfNeeded();
}
});

function writeToModelIfNeeded() {
if (ctrl.$modelValue !== prevModelValue) {
ctrl.$$writeModelToScope();
}
});
}
};

this.$$writeModelToScope = function() {
Expand Down Expand Up @@ -2765,6 +2776,8 @@ var ngValueDirective = function() {
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
* custom value for each event. For example:
* `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
* not validate correctly instead of the default behavior of setting the model to undefined.
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
`ngModel` as getters/setters.
* - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
Expand Down
73 changes: 73 additions & 0 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,79 @@ describe('input', function() {
'ng-model-options="{ getterSetter: true }" />');
});

it('should assign invalid values to the scope if allowInvalid is true', function() {
compileInput('<input type="text" name="input" ng-model="value" maxlength="1" ' +
'ng-model-options="{allowInvalid: true}" />');
changeInputValueTo('12345');

expect(scope.value).toBe('12345');
expect(inputElm).toBeInvalid();
});

it('should not assign not parsable values to the scope if allowInvalid is true', function() {
compileInput('<input type="number" name="input" ng-model="value" ' +
'ng-model-options="{allowInvalid: true}" />', {
valid: false,
badInput: true
});
changeInputValueTo('abcd');

expect(scope.value).toBeUndefined();
expect(inputElm).toBeInvalid();
});

it('should update the scope before async validators execute if allowInvalid is true', inject(function($q) {
compileInput('<input type="text" name="input" ng-model="value" ' +
'ng-model-options="{allowInvalid: true}" />');
var defer;
scope.form.input.$asyncValidators.promiseValidator = function(value) {
defer = $q.defer();
return defer.promise;
};
changeInputValueTo('12345');

expect(scope.value).toBe('12345');
expect(scope.form.input.$pending.promiseValidator).toBe(true);
defer.reject();
scope.$digest();
expect(scope.value).toBe('12345');
expect(inputElm).toBeInvalid();
}));

it('should update the view before async validators execute if allowInvalid is true', inject(function($q) {
compileInput('<input type="text" name="input" ng-model="value" ' +
'ng-model-options="{allowInvalid: true}" />');
var defer;
scope.form.input.$asyncValidators.promiseValidator = function(value) {
defer = $q.defer();
return defer.promise;
};
scope.$apply('value = \'12345\'');

expect(inputElm.val()).toBe('12345');
expect(scope.form.input.$pending.promiseValidator).toBe(true);
defer.reject();
scope.$digest();
expect(inputElm.val()).toBe('12345');
expect(inputElm).toBeInvalid();
}));

it('should not call ng-change listeners twice if the model did not change with allowInvalid', function() {
compileInput('<input type="text" name="input" ng-model="value" ' +
'ng-model-options="{allowInvalid: true}" ng-change="changed()" />');
scope.changed = jasmine.createSpy('changed');
scope.form.input.$parsers.push(function(value) {
return 'modelValue';
});

changeInputValueTo('input1');
expect(scope.value).toBe('modelValue');
expect(scope.changed).toHaveBeenCalledOnce();

changeInputValueTo('input2');
expect(scope.value).toBe('modelValue');
expect(scope.changed).toHaveBeenCalledOnce();
});
});

it('should allow complex reference binding', function() {
Expand Down