-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Fix: set $submitted on child forms when form is submitted #15778
Conversation
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
src/ng/directive/form.js
Outdated
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS); | ||
this.$submitted = true; | ||
this.$$parentForm.$setSubmitted(); | ||
$setSubmitted: function(onlySetOnChildren) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The onlySetOnChildren
param name is misleading as it might imply that we don't set it on this
:-)
How about something about not setting it on the parent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this param even necessary? I don't see it used anywhere?
Perhaps it would be best to leave it out for this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in line 278 - we need it because if we always set it on both parent and children we can go into an infinite loop.
You're right with the parameter name. How about doNotSetOnParent
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh right!
+1 on the new param name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is another boolean trap in a public API, isn't it? :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have quite a lot of boolean parameters in our providers config methods but then they're not so bad as the convention is shared across the library and in all cases it just enables or disables the setting indicated by the method name.
Here there's no obvious relation between the parameter and the method name; it's impossible to know what's happening just by looking at the code. Naming the variable properly only helps us. Note, though, that negative parameters are confusing to people as when you set it to false
you have double negation and it's been scientifically proven that it's harder for people to process.
If this is going to extent the public API, I think we should use an object with a parameter so that it's named (and then rather setOnParent
than doNotSetOnParent
). It's way easier to do it in ES6 but it's still possible in ES5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alternative is to create a new private function. Perhaps something like this:
$setSubmitted: function() {
var rootForm = this;
while(rootForm.$$parentForm) { rootForm = rootForm.$$parentForm; }
rootForm.$$setSubmitted();
}
$$setSubmitted() {
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
this.$submitted = true;
forEach(this.$$controls, function(control) {
if (control.$$setSubmitted && !control.$submitted) {
control.$setSubmitted(false);
}
});
}
@petebacondarwin wdyt about the breaking-changeness of the PR? @gkalpak had some reservations in the previous PR: #11023 (comment) |
src/ng/directive/form.js
Outdated
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS); | ||
this.$submitted = true; | ||
this.$$parentForm.$setSubmitted(); | ||
$setSubmitted: function(onlySetOnChildren) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have quite a lot of boolean parameters in our providers config methods but then they're not so bad as the convention is shared across the library and in all cases it just enables or disables the setting indicated by the method name.
Here there's no obvious relation between the parameter and the method name; it's impossible to know what's happening just by looking at the code. Naming the variable properly only helps us. Note, though, that negative parameters are confusing to people as when you set it to false
you have double negation and it's been scientifically proven that it's harder for people to process.
If this is going to extent the public API, I think we should use an object with a parameter so that it's named (and then rather setOnParent
than doNotSetOnParent
). It's way easier to do it in ES6 but it's still possible in ES5.
Regarding the BC - I guess that it is a breaking change. And this also raises the importance of being able to "detach" or "isolate" subforms from their parent. In which case you could block the child/parent form from being submitted if it was not "attached" to the form on which the setSubmit was called. |
Ok, then let's put into 1.7.0 and rethink the API. |
I'm not sure why this needs to be a breaking change. I'm probably missing something. If the goal is to set child forms to submitted when the parent form is submitted, couldn't you add an attribute to ngForm such as |
@aeslinger0 that is an interesting idea. What do you think @Narretz ? |
Hm, I have to think about it, but in general the form API isn't very consistent as is, and I don't think an option that affects a single part of the API is very elegant. |
beb33a4
to
7787d00
Compare
I've implemented @petebacondarwin 's approach to setting submitted. Detaching the form is easy (just call $removeControl on the parent form), but the behavior that the detached form should have is not. |
Here's a re-cap on what I think would be needed to support "detached" forms, i.e. a form that does not inherit or propagate state from / to its parent form. Requirements:
Problems: 2b) only the form element fires a "submit" event when you press a submit button etc., ngForm does not. This means, if you want to submit detached forms individually, you would have to wrap forms in ngForm. That however would mean, you cannot submit the whole form. Solution: We should also update the docs with the info that in core ngForm, when used standalone, does not support form-specific submit handling. Finally, I would introduce this PR as the default, without an option - if you have nested forms that are not detached, then I find it reasonable that the parent form sets $submitted on child forms. |
I still don't like the idea of detached forms fwiw 😁 |
That's why I would move this whole topic to user-land. I agree that the semantics are unclear, it can become very complex very quickly, we don't know what kind of behavior devs actually want. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR looks good to me as it stands. Let's punt on detached forms for the moment.
Ok, so then I will squash the commits, add a BC notice and merge only into master. |
src/ng/directive/form.js
Outdated
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS); | ||
this.$submitted = true; | ||
this.$$parentForm.$setSubmitted(); | ||
forEach(this.$$controls, function(control) { | ||
if (control.$$setSubmitted && !control.$submitted) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the !control.$submitted
check? Under what circumstances can a sub-form be $submitted
(while the parent form is not) and does that guarantee that all sub-forms of the sub-form are also $submitted
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, the form could have been removed with "removeControl", then have been changed to submitted via "setSubmitted" and re-attached to the parent form with "addControl". Unlikely, but possible. I don't think it's worth optimzing this, though. I think I took the implementation from this comment: #15778 (comment)
118a547
to
6bf01af
Compare
…tted Closes angular#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.
44a6659
to
e98bb35
Compare
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Fix
What is the current behavior? (You can also link to an open issue here)
When submitting a form, $submitted is not set on child forms
Does this PR introduce a breaking change?
I don't think so.
No one in #10071 said that his might be breaking.
AngularJS doesn't have nested form isolation, and ngForm is only for grouping sub-forms. I think that it's expected that a submission event will affect all sub-forms.
Probably: #11023 (comment)
Please check if the PR fulfills these requirements
Follow up to #11023