diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js
index d14e7f6d6933..6ad762eea193 100644
--- a/src/ng/directive/input.js
+++ b/src/ng/directive/input.js
@@ -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)) {
@@ -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();
+ }
}
}
};
@@ -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
diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js
index 8ac1350000aa..3112734ca956 100644
--- a/test/ng/directive/inputSpec.js
+++ b/test/ng/directive/inputSpec.js
@@ -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('');
+ 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('', {
+ 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('');
+ 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('');
+ 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() {