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

Commit 223de59

Browse files
committed
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.
1 parent 5c38fb7 commit 223de59

File tree

2 files changed

+124
-3
lines changed

2 files changed

+124
-3
lines changed

Diff for: src/ng/directive/form.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ var nullFormCtrl = {
99
$setValidity: noop,
1010
$setDirty: noop,
1111
$setPristine: noop,
12-
$setSubmitted: noop
12+
$setSubmitted: noop,
13+
$$setSubmitted: noop
1314
},
1415
PENDING_CLASS = 'ng-pending',
1516
SUBMITTED_CLASS = 'ng-submitted';
@@ -274,12 +275,25 @@ FormController.prototype = {
274275
* @name form.FormController#$setSubmitted
275276
*
276277
* @description
277-
* Sets the form to its submitted state.
278+
* Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
279+
* parent forms of the form.
278280
*/
279281
$setSubmitted: function() {
282+
var rootForm = this;
283+
while (rootForm.$$parentForm && (rootForm.$$parentForm !== nullFormCtrl)) {
284+
rootForm = rootForm.$$parentForm;
285+
}
286+
rootForm.$$setSubmitted();
287+
},
288+
289+
$$setSubmitted: function() {
280290
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
281291
this.$submitted = true;
282-
this.$$parentForm.$setSubmitted();
292+
forEach(this.$$controls, function(control) {
293+
if (control.$$setSubmitted) {
294+
control.$$setSubmitted();
295+
}
296+
});
283297
}
284298
};
285299

Diff for: test/ng/directive/formSpec.js

+107
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,113 @@ describe('form', function() {
539539
expect(parent.$submitted).toBeTruthy();
540540
});
541541

542+
it('should set $submitted to true on child forms when parent is submitted', function() {
543+
doc = jqLite(
544+
'<ng-form name="parent">' +
545+
'<ng-form name="child">' +
546+
'<input ng:model="modelA" name="inputA">' +
547+
'<input ng:model="modelB" name="inputB">' +
548+
'</ng-form>' +
549+
'</ng-form>');
550+
$compile(doc)(scope);
551+
552+
var parent = scope.parent,
553+
child = scope.child;
554+
555+
parent.$setSubmitted();
556+
expect(parent.$submitted).toBeTruthy();
557+
expect(child.$submitted).toBeTruthy();
558+
});
559+
560+
561+
it('should not propagate $submitted state on removed child forms when parent is submitted', function() {
562+
doc = jqLite(
563+
'<ng-form name="parent">' +
564+
'<ng-form name="child">' +
565+
'<ng-form name="grandchild">' +
566+
'<input ng:model="modelA" name="inputA">' +
567+
'</ng-form>' +
568+
'</ng-form>' +
569+
'</ng-form>');
570+
$compile(doc)(scope);
571+
572+
var parent = scope.parent,
573+
child = scope.child,
574+
grandchild = scope.grandchild,
575+
ggchild = scope.greatgrandchild;
576+
577+
parent.$removeControl(child);
578+
579+
parent.$setSubmitted();
580+
expect(parent.$submitted).toBeTruthy();
581+
expect(child.$submitted).not.toBeTruthy();
582+
expect(grandchild.$submitted).not.toBeTruthy();
583+
584+
parent.$addControl(child);
585+
586+
expect(parent.$submitted).toBeTruthy();
587+
expect(child.$submitted).not.toBeTruthy();
588+
expect(grandchild.$submitted).not.toBeTruthy();
589+
590+
parent.$setSubmitted();
591+
expect(parent.$submitted).toBeTruthy();
592+
expect(child.$submitted).toBeTruthy();
593+
expect(grandchild.$submitted).toBeTruthy();
594+
595+
parent.$removeControl(child);
596+
597+
expect(parent.$submitted).toBeTruthy();
598+
expect(child.$submitted).toBeTruthy();
599+
expect(grandchild.$submitted).toBeTruthy();
600+
601+
parent.$setPristine(); // sets $submitted to false
602+
expect(parent.$submitted).not.toBeTruthy();
603+
expect(child.$submitted).toBeTruthy();
604+
expect(grandchild.$submitted).toBeTruthy();
605+
606+
grandchild.$setPristine();
607+
expect(grandchild.$submitted).not.toBeTruthy();
608+
609+
child.$setSubmitted();
610+
expect(parent.$submitted).not.toBeTruthy();
611+
expect(child.$submitted).toBeTruthy();
612+
expect(grandchild.$submitted).toBeTruthy();
613+
614+
child.$setPristine();
615+
expect(parent.$submitted).not.toBeTruthy();
616+
expect(child.$submitted).not.toBeTruthy();
617+
expect(grandchild.$submitted).not.toBeTruthy();
618+
619+
// Test upwards submission setting
620+
grandchild.$setSubmitted();
621+
expect(parent.$submitted).not.toBeTruthy();
622+
expect(child.$submitted).toBeTruthy();
623+
expect(grandchild.$submitted).toBeTruthy();
624+
});
625+
626+
627+
it('should set $submitted to true on child and parent forms when form is submitted', function() {
628+
doc = jqLite(
629+
'<ng-form name="parent">' +
630+
'<ng-form name="child">' +
631+
'<ng-form name="grandchild">' +
632+
'<input ng:model="modelA" name="inputA">' +
633+
'<input ng:model="modelB" name="inputB">' +
634+
'</ng-form>' +
635+
'</ng-form>' +
636+
'</ng-form>');
637+
$compile(doc)(scope);
638+
639+
var parent = scope.parent,
640+
child = scope.child,
641+
grandchild = scope.grandchild;
642+
643+
child.$setSubmitted();
644+
645+
expect(parent.$submitted).toBeTruthy();
646+
expect(child.$submitted).toBeTruthy();
647+
expect(grandchild.$submitted).toBeTruthy();
648+
});
542649

543650
it('should deregister a child form when its DOM is removed', function() {
544651
doc = jqLite(

0 commit comments

Comments
 (0)