From 1cc1b85ede530580d3eb7f6f83ea17c5ff6b3ac9 Mon Sep 17 00:00:00 2001 From: Shahar Talmi Date: Thu, 24 Jul 2014 03:04:51 +0300 Subject: [PATCH] feat(ngModelOptions): add allowInvalid option This option allows to write invalid values to the model instead of having them become undefined. Closes #8290 --- src/ng/directive/input.js | 16 +++++++--- test/ng/directive/inputSpec.js | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) 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() {