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

feat(ngModelOptions): add allowInvalid option #8313

Closed
wants to merge 1 commit into from
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
16 changes: 12 additions & 4 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1893,7 +1893,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
var localValidationRunId = currentValidationRunId;

// We can update the $$invalidModelValue immediately as we don't have to wait for validators!
ctrl.$$invalidModelValue = modelValue;
if (ctrl.$options && ctrl.$options.allowInvalid) {
ctrl.$modelValue = modelValue;
doneCallback();
} else {
ctrl.$$invalidModelValue = modelValue;
}

// check parser error
if (!processParseErrors(parseValid)) {
Expand Down Expand Up @@ -1972,9 +1977,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
function validationDone() {
if (localValidationRunId === currentValidationRunId) {
// set the validated model value
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;

doneCallback();
if (!ctrl.$options || !ctrl.$options.allowInvalid) {
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
doneCallback();
}
}
}
};
Expand Down Expand Up @@ -2751,6 +2757,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
56 changes: 56 additions & 0 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1683,6 +1683,62 @@ 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 allow complex reference binding', function() {
Expand Down