fix(form): set $submitted to true on child forms when parent is submitted

Closes #10071

BREAKING CHANGE:

Forms will now set $submitted on child forms when they are submitted.
For example:
```
<form name="parentform" ng-submit="$ctrl.submit()">
  <ng-form name="childform">
    <input type="text" name="input" ng-model="my.model" />
  </ng-form>
  <input type="submit" />
</form>

Submitting this form will set $submitted on "parentform" and "childform".
Previously, it was only set on "parentform".

This change was introduced because mixing form and ngForm does not create
logically separate forms, but rather something like input groups.
Therefore, child forms should inherit the submission state from their parent form.
This commit is contained in:
Martin Staffa
2017-12-12 12:45:44 +01:00
committed by Martin Staffa
parent 5c38fb744e
commit 223de59e98
2 changed files with 124 additions and 3 deletions
+17 -3
View File
@@ -9,7 +9,8 @@ var nullFormCtrl = {
$setValidity: noop,
$setDirty: noop,
$setPristine: noop,
$setSubmitted: noop
$setSubmitted: noop,
$$setSubmitted: noop
},
PENDING_CLASS = 'ng-pending',
SUBMITTED_CLASS = 'ng-submitted';
@@ -274,12 +275,25 @@ FormController.prototype = {
* @name form.FormController#$setSubmitted
*
* @description
* Sets the form to its submitted state.
* Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
* parent forms of the form.
*/
$setSubmitted: function() {
var rootForm = this;
while (rootForm.$$parentForm && (rootForm.$$parentForm !== nullFormCtrl)) {
rootForm = rootForm.$$parentForm;
}
rootForm.$$setSubmitted();
},
$$setSubmitted: function() {
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
this.$submitted = true;
this.$$parentForm.$setSubmitted();
forEach(this.$$controls, function(control) {
if (control.$$setSubmitted) {
control.$$setSubmitted();
}
});
}
};
+107
View File
@@ -539,6 +539,113 @@ describe('form', function() {
expect(parent.$submitted).toBeTruthy();
});
it('should set $submitted to true on child forms when parent is submitted', function() {
doc = jqLite(
'<ng-form name="parent">' +
'<ng-form name="child">' +
'<input ng:model="modelA" name="inputA">' +
'<input ng:model="modelB" name="inputB">' +
'</ng-form>' +
'</ng-form>');
$compile(doc)(scope);
var parent = scope.parent,
child = scope.child;
parent.$setSubmitted();
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).toBeTruthy();
});
it('should not propagate $submitted state on removed child forms when parent is submitted', function() {
doc = jqLite(
'<ng-form name="parent">' +
'<ng-form name="child">' +
'<ng-form name="grandchild">' +
'<input ng:model="modelA" name="inputA">' +
'</ng-form>' +
'</ng-form>' +
'</ng-form>');
$compile(doc)(scope);
var parent = scope.parent,
child = scope.child,
grandchild = scope.grandchild,
ggchild = scope.greatgrandchild;
parent.$removeControl(child);
parent.$setSubmitted();
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).not.toBeTruthy();
expect(grandchild.$submitted).not.toBeTruthy();
parent.$addControl(child);
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).not.toBeTruthy();
expect(grandchild.$submitted).not.toBeTruthy();
parent.$setSubmitted();
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
parent.$removeControl(child);
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
parent.$setPristine(); // sets $submitted to false
expect(parent.$submitted).not.toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
grandchild.$setPristine();
expect(grandchild.$submitted).not.toBeTruthy();
child.$setSubmitted();
expect(parent.$submitted).not.toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
child.$setPristine();
expect(parent.$submitted).not.toBeTruthy();
expect(child.$submitted).not.toBeTruthy();
expect(grandchild.$submitted).not.toBeTruthy();
// Test upwards submission setting
grandchild.$setSubmitted();
expect(parent.$submitted).not.toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
});
it('should set $submitted to true on child and parent forms when form is submitted', function() {
doc = jqLite(
'<ng-form name="parent">' +
'<ng-form name="child">' +
'<ng-form name="grandchild">' +
'<input ng:model="modelA" name="inputA">' +
'<input ng:model="modelB" name="inputB">' +
'</ng-form>' +
'</ng-form>' +
'</ng-form>');
$compile(doc)(scope);
var parent = scope.parent,
child = scope.child,
grandchild = scope.grandchild;
child.$setSubmitted();
expect(parent.$submitted).toBeTruthy();
expect(child.$submitted).toBeTruthy();
expect(grandchild.$submitted).toBeTruthy();
});
it('should deregister a child form when its DOM is removed', function() {
doc = jqLite(