diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js index cc97411a4c3e..1c37167db193 100644 --- a/src/ng/directive/input.js +++ b/src/ng/directive/input.js @@ -1222,7 +1222,7 @@ var ngModelDirective = function() { formCtrl.$addControl(modelCtrl); - element.on('$destroy', function() { + scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js index 53fd3d907432..77beb2fd64ef 100644 --- a/test/ng/directive/formSpec.js +++ b/test/ng/directive/formSpec.js @@ -36,18 +36,23 @@ describe('form', function() { }); - it('should remove the widget when element removed', function() { + it('should remove form control references from the form when nested control is removed from the DOM', function() { doc = $compile( '
')(scope); + scope.inputPresent = true; + scope.$digest(); var form = scope.myForm; control.$setValidity('required', false); expect(form.alias).toBe(control); expect(form.$error.required).toEqual([control]); - doc.find('input').remove(); + // remove nested control + scope.inputPresent = false; + scope.$apply(); + expect(form.$error.required).toBe(false); expect(form.alias).toBeUndefined(); }); @@ -362,14 +367,15 @@ describe('form', function() { }); - it('should deregister a input when its removed from DOM', function() { + it('should deregister a input when it is removed from DOM', function() { doc = jqLite( ''); $compile(doc)(scope); + scope.inputPresent = true; scope.$apply(); var parent = scope.parent, @@ -384,7 +390,10 @@ describe('form', function() { expect(doc.hasClass('ng-invalid-required')).toBe(true); expect(doc.find('div').hasClass('ng-invalid')).toBe(true); expect(doc.find('div').hasClass('ng-invalid-required')).toBe(true); - doc.find('input').remove(); //remove child + + //remove child input + scope.inputPresent = false; + scope.$apply(); expect(parent.$error.required).toBe(false); expect(child.$error.required).toBe(false); diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index c60960f06312..3783c9edcec7 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -305,6 +305,84 @@ describe('ngModel', function() { expect(element).toBeInvalid(); expect(element).toHaveClass('ng-invalid-required'); })); + + + it('should register/deregister a nested ngModel with parent form when entering or leaving DOM', + inject(function($compile, $rootScope) { + + var element = $compile('')($rootScope); + var isFormValid; + + $rootScope.inputPresent = false; + $rootScope.$watch('myForm.$valid', function(value) { isFormValid = value; }); + + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(true); + expect(isFormValid).toBe(true); + expect($rootScope.myForm.myControl).toBeUndefined(); + + $rootScope.inputPresent = true; + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(false); + expect(isFormValid).toBe(false); + expect($rootScope.myForm.myControl).toBeDefined(); + + $rootScope.inputPresent = false; + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(true); + expect(isFormValid).toBe(true); + expect($rootScope.myForm.myControl).toBeUndefined(); + + dealoc(element); + })); + + + it('should register/deregister a nested ngModel with parent form when entering or leaving DOM with animations', + function() { + + // ngAnimate performs the dom manipulation after digest, and since the form validity can be affected by a form + // control going away we must ensure that the deregistration happens during the digest while we are still doing + // dirty checking. + module('ngAnimate'); + + inject(function($compile, $rootScope) { + var element = $compile('')($rootScope); + var isFormValid; + + $rootScope.inputPresent = false; + // this watch ensure that the form validity gets updated during digest (so that we can observe it) + $rootScope.$watch('myForm.$valid', function(value) { isFormValid = value; }); + + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(true); + expect(isFormValid).toBe(true); + expect($rootScope.myForm.myControl).toBeUndefined(); + + $rootScope.inputPresent = true; + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(false); + expect(isFormValid).toBe(false); + expect($rootScope.myForm.myControl).toBeDefined(); + + $rootScope.inputPresent = false; + $rootScope.$apply(); + + expect($rootScope.myForm.$valid).toBe(true); + expect(isFormValid).toBe(true); + expect($rootScope.myForm.myControl).toBeUndefined(); + + dealoc(element); + }); + }); }); @@ -369,17 +447,6 @@ describe('input', function() { }); - it('should cleanup it self from the parent form', function() { - compileInput(''); - - scope.$apply(); - expect(scope.form.$error.required.length).toBe(1); - - inputElm.remove(); - expect(scope.form.$error.required).toBe(false); - }); - - it('should update the model on "blur" event', function() { compileInput('');