-
Notifications
You must be signed in to change notification settings - Fork 27.4k
ng-if around a single radio button removes the entire ngModelController from the formController #15009
Comments
It is not obvious in your example (here is a slightly better illustration), but while there are 3 "child" This happens, because each control will assign its model controller on the parent form (if one exists), on a property that matches the value of the control's As expected, since But why is the 2nd child overwriting the other two? Normally, it would be the 3rd "child" control overwriting the other two, because the last control to register wins. Yet, because of the Conclusion(s):
Possible Actions:
|
tl;dr |
But in the special case of radio buttons they must have the same name, since they are bound to one model, and hence, to one model controller, correct? I've tried now to give them different names. The result is two different radio button groups: one containing child1 and child3, and one containing child2. And of course there are three references to the same model controller on the form, which is sub-optimal. https://plnkr.co/edit/MNrze43iWbwGUtajWSA3?p=preview I think different names would bring up even more issues than it would solve. And perhaps more importantly, doesn't angular want to keep to the W3C HTML spec where radio "name" attribute is used as a key for the radio button group? |
Nope. Each element gets its own
Nope. In reality, there are 3 different radio button groups for the 3 "child" radios (as far as browser understands). But in Angular you don't care about that, because it is the The reason it seems like there are two "child" groups, is related to how Scope works and is not relevant. Make sure to follow the dot rule for models: The expressions you bind to must contain at least one dot (e.g.:
Nope. There are three references to three different
I don't think so. In fact, Angular will create a unique name for each
I don't think there is any such thing as "radio button group" in HTML. According to the spec, the
In Angular, form submission is totally different, so we don't need the name, because So, like I said in my previous comment, I don't see where having same names for different controls in an Angular form would be useful. Btw, you can create a custom directive that does decouple the name attribute from the name under which the control is registered with its parent form. A very naive approach is shown here. |
I haven't read everything, but in Angular you still need to give every radio button that belongs to a single choice the same name. Because the checking behavior depends on the browser: if radio (a) gets checked, then radio (b) gets unchecked if both have the same name. So if one radio has ngIf around it, is added later than all other radios with the same name. That means it'll overwrite any previous ngModelControllers with the same name on the parent form controller. If you remove it via ngIf, it is removed from the parent form, but since we do not special case input radio, we do not re-add any of the still existing radios with the same name. |
Thank you both for your answers, even though they contradict each other. I agree with Narrets regarding the name (keeping in line with HTML, nowhere else have I seen that angular requires different names for radio buttons), even though this is not the point of this thread. The point being: If you guys consider this not a bug, could either of you provide a plunker where there is a working example of an ng-if around a radio button? I haven't found a way to make this work. It works for all other controls. If it cannot be done then I would say it is either an agreed specific limitation for radio buttons and should be mentioned in angular docs, or it is a bug. Would you agree? |
I first wanted to get some consensus on the issue here, before I re-categorize this issue. So let's wait and see what @gkalpak thinks about the new info. |
@Narretz, no it is not necessary in Angular, because
We definitely shouldn't do that (re-add existing
Typically, it doesn't (it will work just fine with identical names). BUT, since
Everything works as expected if you give your radios different names (or no names at all).
It only works if you give your controls different names. The exact same applies to radios.
|
That said, I wouldn't mind having a built-in Maybe we should add a note to the docs, because it is indeed probable that people new to Angular will try to put the same names on their radios. |
You are right, you don't have to use the same name. I remember I has some issues with this a long time ago, so I thought you can't do individual names. But I think it's very probable that devs will give the radios the same name, because that's how you do it without angular - and it works in most cases, so there's no indication not to do it. So maybe add something to the docs, saying that you don't have to use the same name, and you should use unique names when your radios are dynamically added / removed. |
Thanks gkalpak for the detailed explanation. The ng-if is indeed no issue with different names. I completely understand the technicality behind your answer regarding names. Do you mind If we try to step back from technicality and see a bigger picture for a moment? I was under the impression that Angular tries very hard to build on top of HTML and not to change its core usage (is that correct? I hope so..). The overall value is huge - more intuitive framework, less need for documentation because the code documents itself, not just for framework code but also for usage code. If the official instruction is to use different names for radio buttons of the same [name-value] nature, then this is 'breaking' a very common usage of HTML forms. Is keeping HTML in tact not a core value for Angular? Was this just my assumption? (I understand that radio buttons can still work the HTML way, but you officially advise against it, and it causes my original bug, so I hope you see that this is not a satisfying answer in this case.) A small additional question that comes to mind is, when using ng-messages for a radio button, are developers supposed to point to any arbitrary name that is bound to that model? I guess this would work, but again seems not very intuitive and would also look somewhat peculiar (which, in turn, may cause more maintenance overhead). I very much hope my comment does not seem antagonising or petty, it is meant to be neither. |
By the way:
There is, actually: And I guess my tl;dr post version is "and boy oh boy how nice it would be if Angular would consider implementing it similarly, even though technically they don't have to." 😃 |
@tsemer we aren't actually supporting a radio group correctly, as our radio binding extends over the boundaries of forms. (It's restricted by scope boundaries). |
Thanks @Narretz. That sounds a bit scary, do I understand correctly that within the same scope, two forms containing radio buttons with the same bound model would behave like they are on a single form? Do you consider it something that you wish to fix, for any milestone? |
@tsemer It was raised once before iirc, but it's not something that actually affects many people in practice. |
@Narretz wrote:
I agree. At this stage in the life of AngularJS there is little benefit in implementing such a considerable breaking change. |
Let's leave this open as a marker for adding docs about this. |
I have created #16478 to document the current situation. Note that — even if should not be necessary in AngularJS-controlled forms (as explained above) — implementing an "ngName" directive yourself (i.e. a directive that allows you to use the name .directive('ngName', () => ({
link: {pre: (s, e, attrs) => attrs.$observe('ngName', name => attrs.$set('name', name))},
priority: 2,
restrict: 'A',
})) Then, use it like this: <form name="myForm">
<label>
<input type="radio" name="same-name" ng-name="radio1" value="foo" ng-model="data.choice" />
Foo
</label>
<label>
<input type="radio" name="same-name" ng-name="radio2" value="bar" ng-model="data.choice" />
Bar
</label>
</form>
<pre>1st NgModelController: {{ myForm.radio1 | json }}</pre>
<pre>2nd NgModelController: {{ myForm.radio2 | json }}</pre> |
Currently, when wrapping a single radio button input inside a ng-if, and when the ng-if expression evaluates to "false", the ngModelController is removed entirely from the formController.
Plunker:
https://plnkr.co/edit/6KwTDlydGdC1VHtLoszg?p=preview
A workaround is to use ng-show, but sometimes ng-if is preferable, and should be able to work as well. The current situation also baffles developers as it is non-trivial.
Angular version: 1.5.7
The text was updated successfully, but these errors were encountered: