aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2012-04-03 22:05:13 -0700
committerMisko Hevery2012-04-03 23:28:05 -0700
commit21b77ad5c231ab0e05eb89f22005f7ed8d40a6c1 (patch)
tree3425cfb53994ff0bc7a321144099a6a811f97259
parent2f5dba488e855bcdbb9304aa809efcb9de7b43e9 (diff)
downloadangular.js-21b77ad5c231ab0e05eb89f22005f7ed8d40a6c1.tar.bz2
fix(form): preperly clean up when invalid widget is removed
Removing invalid widget sometimes resulted in improper cleanup of the form state.
-rw-r--r--src/ng/directive/form.js61
-rw-r--r--test/ng/directive/formSpec.js43
2 files changed, 66 insertions, 38 deletions
diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js
index 7b96c936..f688b542 100644
--- a/src/ng/directive/form.js
+++ b/src/ng/directive/form.js
@@ -70,23 +70,43 @@ function FormController(element, attrs) {
if (control.$name && form[control.$name] === control) {
delete form[control.$name];
}
- forEach(errors, cleanupControlErrors, control);
+ forEach(errors, function(queue, validationToken) {
+ form.$setValidity(validationToken, true, control);
+ });
};
form.$setValidity = function(validationToken, isValid, control) {
- if (isValid) {
- cleanupControlErrors(errors[validationToken], validationToken, control);
+ var queue = errors[validationToken];
- if (!invalidCount) {
- toggleValidCss(isValid);
- form.$valid = true;
- form.$invalid = false;
+ if (isValid) {
+ if (queue) {
+ arrayRemove(queue, control);
+ if (!queue.length) {
+ invalidCount--;
+ if (!invalidCount) {
+ toggleValidCss(isValid);
+ form.$valid = true;
+ form.$invalid = false;
+ }
+ errors[validationToken] = false;
+ toggleValidCss(true, validationToken);
+ parentForm.$setValidity(validationToken, true, form);
+ }
}
+
} else {
if (!invalidCount) {
toggleValidCss(isValid);
}
- addControlError(validationToken, control);
+ if (queue) {
+ if (includes(queue, control)) return;
+ } else {
+ errors[validationToken] = queue = [];
+ invalidCount++;
+ toggleValidCss(false, validationToken);
+ parentForm.$setValidity(validationToken, false, form);
+ }
+ queue.push(control);
form.$valid = false;
form.$invalid = true;
@@ -99,31 +119,6 @@ function FormController(element, attrs) {
form.$pristine = false;
};
- function cleanupControlErrors(queue, validationToken, control) {
- if (queue) {
- control = control || this; // so that we can be used in forEach;
- arrayRemove(queue, control);
- if (!queue.length) {
- invalidCount--;
- errors[validationToken] = false;
- toggleValidCss(true, validationToken);
- parentForm.$setValidity(validationToken, true, form);
- }
- }
- }
-
- function addControlError(validationToken, control) {
- var queue = errors[validationToken];
- if (queue) {
- if (includes(queue, control)) return;
- } else {
- errors[validationToken] = queue = [];
- invalidCount++;
- toggleValidCss(false, validationToken);
- parentForm.$setValidity(validationToken, false, form);
- }
- queue.push(control);
- }
}
diff --git a/test/ng/directive/formSpec.js b/test/ng/directive/formSpec.js
index 491a31db..3e672a7b 100644
--- a/test/ng/directive/formSpec.js
+++ b/test/ng/directive/formSpec.js
@@ -189,16 +189,16 @@ describe('form', function() {
it('should deregister a child form when its DOM is removed', function() {
doc = jqLite(
- '<form name="parent">' +
- '<div class="ng-form" name="child">' +
- '<input ng:model="modelA" name="inputA" required>' +
- '</div>' +
+ '<form name="parent">' +
+ '<div class="ng-form" name="child">' +
+ '<input ng:model="modelA" name="inputA" required>' +
+ '</div>' +
'</form>');
$compile(doc)(scope);
scope.$apply();
var parent = scope.parent,
- child = scope.child;
+ child = scope.child;
expect(parent).toBeDefined();
expect(child).toBeDefined();
@@ -211,6 +211,39 @@ describe('form', function() {
});
+ it('should deregister a input when its removed from DOM', function() {
+ doc = jqLite(
+ '<form name="parent">' +
+ '<div class="ng-form" name="child">' +
+ '<input ng:model="modelA" name="inputA" required>' +
+ '</div>' +
+ '</form>');
+ $compile(doc)(scope);
+ scope.$apply();
+
+ var parent = scope.parent,
+ child = scope.child,
+ input = child.inputA;
+
+ expect(parent).toBeDefined();
+ expect(child).toBeDefined();
+ expect(parent.$error.required).toEqual([child]);
+ expect(child.$error.required).toEqual([input]);
+ expect(doc.hasClass('ng-invalid')).toBe(true);
+ 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
+
+ expect(parent.$error.required).toBe(false);
+ expect(child.$error.required).toBe(false);
+ expect(doc.hasClass('ng-valid')).toBe(true);
+ expect(doc.hasClass('ng-valid-required')).toBe(true);
+ expect(doc.find('div').hasClass('ng-valid')).toBe(true);
+ expect(doc.find('div').hasClass('ng-valid-required')).toBe(true);
+ });
+
+
it('should chain nested forms in repeater', function() {
doc = jqLite(
'<ng:form name=parent>' +