Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(form): set $submitted to true on child forms when parent is submi…
Browse files Browse the repository at this point in the history
…tted

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.
  • Loading branch information
Narretz committed Dec 13, 2017
1 parent 5c38fb7 commit 223de59
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 3 deletions.
20 changes: 17 additions & 3 deletions src/ng/directive/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
}
});
}
};

Expand Down
107 changes: 107 additions & 0 deletions test/ng/directive/formSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 223de59

Please sign in to comment.