From d9a6dc054e40044a61a43a6c83c1c8b64408722f Mon Sep 17 00:00:00 2001 From: Viacheslav Vasianovych Date: Mon, 9 Feb 2015 10:43:52 -0500 Subject: [PATCH 1/2] feat (ngModel, form): add 'reset all errors' functionality reset all errors added with $setValidity method. It is usefull functionality in case when you need to reset all errors before validating form again. --- src/ng/directive/form.js | 24 +++++++++++++ src/ng/directive/ngModel.js | 62 ++++++++++++++++++++++++++++++-- test/ng/directive/formSpec.js | 53 +++++++++++++++++++++++++++ test/ng/directive/ngModelSpec.js | 48 +++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js index 3041b3fc93fd..54e328b1e032 100644 --- a/src/ng/directive/form.js +++ b/src/ng/directive/form.js @@ -7,6 +7,7 @@ var nullFormCtrl = { $$renameControl: nullFormRenameControl, $removeControl: noop, $setValidity: noop, + $resetErrors: noop, $setDirty: noop, $setPristine: noop, $setSubmitted: noop @@ -183,6 +184,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) { addSetValidityMethod({ ctrl: this, $element: element, + classCache: {}, set: function(object, property, controller) { var list = object[property]; if (!list) { @@ -281,6 +283,28 @@ function FormController(element, attrs, $scope, $animate, $interpolate) { form.$submitted = true; parentForm.$setSubmitted(); }; + + /** + * @ngdoc method + * @name form.FormController#$resetErrors + * + * @description + * Deletes all validities of a form control. + */ + form.$resetErrors = function() { + var errors = copy(form.$error); + forEach(errors, function(fields, validityName) { + forEach(fields, function(field) { + field.$resetErrors(); + cleanUp(form.$error, validityName); + $animate.removeClass(element, INVALID_CLASS + '-' + snake_case(validityName, '-')); + }); + }); + + function cleanUp(object, name) { + delete object[name]; + } + }; } /** diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index 1db2283586c4..e6515cef370d 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -319,6 +319,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ var parentForm = $element.inheritedData('$formController') || nullFormCtrl, currentValidationRunId = 0; + var classCache = {}; /** * @ngdoc method * @name ngModel.NgModelController#$setValidity @@ -344,6 +345,31 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$ addSetValidityMethod({ ctrl: this, $element: $element, + classCache: classCache, + set: function(object, property) { + object[property] = true; + }, + unset: function(object, property) { + delete object[property]; + }, + parentForm: parentForm, + $animate: $animate + }); + + /** + * @ngdoc method + * @name ngModel.NgModelController#$resetErrors + * + * @description + * Reset all errors (added by $setValidity method). + * + * This method should be called in order to reset all ng-invalid-* css classes and all validities added within $setValidity method. + * + */ + addResetErrorsMethod({ + ctrl: this, + $element: $element, + classCache: classCache, set: function(object, property) { object[property] = true; }, @@ -1231,13 +1257,11 @@ var ngModelOptionsDirective = function() { }; }; - - // helper methods function addSetValidityMethod(context) { var ctrl = context.ctrl, $element = context.$element, - classCache = {}, + classCache = context.classCache, set = context.set, unset = context.unset, parentForm = context.parentForm, @@ -1329,6 +1353,38 @@ function addSetValidityMethod(context) { } } +function addResetErrorsMethod(context) { + var ctrl = context.ctrl, + $element = context.$element, + classCache = context.classCache, + set = context.set, + unset = context.unset, + parentForm = context.parentForm, + $animate = context.$animate; + + ctrl.$resetErrors = resetErrors; + + function resetErrors(controller) { + var errors = copy(ctrl.$error); + forEach(errors, function(value, validationName) { + unset(ctrl.$error, validationName, controller); + unset(ctrl.$$success, validationName, controller); + toggleValidationCss(validationName); + }); + } + + function cachedToggleClass(className) { + $animate.removeClass($element, className); + delete classCache[className]; + } + + function toggleValidationCss(validationErrorKey) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + cachedToggleClass(VALID_CLASS + validationErrorKey); + cachedToggleClass(INVALID_CLASS + validationErrorKey); + } +} + function isObjectEmpty(obj) { if (obj) { for (var prop in obj) { diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index d69fa707b6ad..60f5c3ba0b45 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -1000,3 +1000,56 @@ describe('form animations', function() { assertValidAnimation($animate.queue[3], 'removeClass', 'ng-invalid-custom-error'); })); }); + +describe('reset errors', function() { + + var doc, scope, form; + beforeEach(inject(function($rootScope, $compile, $rootElement, $animate) { + scope = $rootScope.$new(); + doc = jqLite('
'); + $rootElement.append(doc); + $compile(doc)(scope); + $animate.queue = []; + form = scope.myForm; + })); + + afterEach(function() { + dealoc(doc); + }); + + function expectClear() { + expect(form.$$success).toEqual({}); + expect(form.$error).toEqual({}); + expect(form.alias.$error).toEqual({}); + } + + function expectError() { + expect(form.$error.someError.length).toBe(1); + expect(form.alias.$error).toEqual({someError: true}); + expect(form.$$success).toEqual({}); + } + + function expectTwoErrors() { + expect(form.$error.someError1.length).toBe(1); + expect(form.$error.someError2.length).toBe(1); + expect(form.alias.$error).toEqual({someError1: true, someError2: true}); + expect(form.$$success).toEqual({}); + } + + it('should trigger an animation when invalid', function() { + expectClear(); + + form.alias.$setValidity('someError', false); + expectError(); + + form.$resetErrors(); + expectClear(); + + form.alias.$setValidity('someError1', false); + form.alias.$setValidity('someError2', false); + expectTwoErrors(); + + form.$resetErrors(); + expectClear(); + }); +}); diff --git a/test/ng/directive/ngModelSpec.js b/test/ng/directive/ngModelSpec.js index 583414deaa9c..e6791cbcdfdc 100644 --- a/test/ng/directive/ngModelSpec.js +++ b/test/ng/directive/ngModelSpec.js @@ -2263,3 +2263,51 @@ describe('ngModelOptions attributes', function() { expect($rootScope.changed).toHaveBeenCalledOnce(); }); }); + +describe('reset errors', function() { + + function assertValidAnimation(animation, event, classNameA, classNameB) { + expect(animation.event).toBe(event); + expect(animation.args[1]).toBe(classNameA); + if (classNameB) expect(animation.args[2]).toBe(classNameB); + } + + var doc, input, scope, model; + + beforeEach(inject(function($rootScope, $compile, $rootElement, $animate) { + scope = $rootScope.$new(); + doc = jqLite('
' + + ' ' + + '
'); + $rootElement.append(doc); + $compile(doc)(scope); + $animate.queue = []; + + input = doc.find('input'); + model = scope.myForm.myInput; + })); + + afterEach(function() { + dealoc(input); + }); + + function expectErrors(errors) { + for (var index = 0; index < errors.length; index++) { + var currentError = errors[index]; + expect(model.$error[currentError]).toBe(true); + } + } + + function expectClear() { + expect(model.$error).toEqual({}); + } + + it('should clear all errors', inject(function($animate) { + model.$setValidity('required', false); + model.$setValidity('onlyNumbers', false); + expectErrors(['required', 'onlyNumbers']); + model.$resetErrors(); + expectClear(); + + })); +}); From 15751c83a7ae9e4f3b3dd2ad15f972e292daa46a Mon Sep 17 00:00:00 2001 From: Viacheslav Vasianovych Date: Tue, 10 Feb 2015 11:45:55 -0500 Subject: [PATCH 2/2] feat (ngModel, form): add 'reset all errors' functionality formatting fixes --- src/ng/directive/ngModel.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/ngModel.js b/src/ng/directive/ngModel.js index e6515cef370d..da4d88a7b871 100644 --- a/src/ng/directive/ngModel.js +++ b/src/ng/directive/ngModel.js @@ -1367,9 +1367,9 @@ function addResetErrorsMethod(context) { function resetErrors(controller) { var errors = copy(ctrl.$error); forEach(errors, function(value, validationName) { - unset(ctrl.$error, validationName, controller); - unset(ctrl.$$success, validationName, controller); - toggleValidationCss(validationName); + unset(ctrl.$error, validationName, controller); + unset(ctrl.$$success, validationName, controller); + toggleValidationCss(validationName); }); }