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

Fix: set $submitted on child forms when form is submitted #15778

Merged
merged 2 commits into from
Dec 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 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 Expand Up @@ -338,16 +352,21 @@ addSetValidityMethod({
* @restrict EAC
*
* @description
* Nestable alias of {@link ng.directive:form `form`} directive. HTML
* does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
* sub-group of controls needs to be determined.
*
* Note: the purpose of `ngForm` is to group controls,
* but not to be a replacement for the `<form>` tag with all of its capabilities
* (e.g. posting to the server, ...).
*
* @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
* related scope, under this name.
* Helper directive that makes it possible to create control groups inside a
* {@link ng.directive:form `form`} directive.
* These "child forms" can be used, for example, to determine the validity of a sub-group of
* controls.
*
* <div class="alert alert-danger">
* **Note**: `ngForm` cannot be used as a replacement for `<form>`, because it lacks its
* [built-in HTML functionality](https://html.spec.whatwg.org/#the-form-element).
* Specifically, you cannot submit `ngForm` like a `<form>` tag. That means,
* you cannot send data to the server with `ngForm`, or integrate it with
* {@link ng.directive:ngSubmit `ngSubmit`}.
* </div>
*
* @param {string=} ngForm|name Name of the form. If specified, the form controller will
* be published into the related scope, under this name.
*
*/

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