Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I'd like to be able to use ngModel without specifying a name #9230

Closed
StephenFluin opened this issue Jun 15, 2016 · 32 comments
Closed

I'd like to be able to use ngModel without specifying a name #9230

StephenFluin opened this issue Jun 15, 2016 · 32 comments
Assignees
Labels
area: forms feature Issue that requests a new feature
Milestone

Comments

@StephenFluin
Copy link
Contributor

feature request

Current behavior
If opted into the new forms and I have code like this:

<form (submit)="process()">
 <input [(ngModel)]="propertyName">
</form>

And this throws an error: ORIGINAL EXCEPTION: Name attribute must be set if ngModel is used within a form.

Expected/desired behavior
I'd like to be able to use html forms and submit capabilities (such as submitting with the enter key) without having to specify a name (and use FormGroups and FormControls) for my ngModel bound inputs.

  • Angular version: 2.0.0-beta.RC2
@IgorMinar
Copy link
Contributor

@kara @vsavkin this sounds reasonable to me. do you have any objections?

@kara
Copy link
Contributor

kara commented Jun 15, 2016

We considered this. The problem is that your forms will no longer behave predictably. In the example above, what would form.value be? There's no way to refer to the input you've added, so it would have to be {undefined: 'someval'}. If you add something like name="first", then the form's value would be {first: 'someval'}.

An alternative might be to have the form avoid registering any ngModels without names, but that seems more confusing. Some inputs simply wouldn't appear in the aggregate form value.

(It's worth noting that outside of a form tag, you can use ngModel without a name.)

I'd love to hear some more opinions on this. Thoughts?

@mmrath
Copy link

mmrath commented Jun 17, 2016

https://plnkr.co/edit/CcXGHjI6jkkW8YpR28ZI?p=preview

I think this is a valid case, specially when I don't want to have the form.value to contain the value specified by [(ngModel)]. I have at least two forms which relied on this to work. But with new form it no longer works. ngModel works outside of a form without a name, but then I had to rearrange the controls in form just so that, the controls are in form and ngModel without name outside of form. But it does not look good to me.

@wrldtrvllr
Copy link

How should we deal with dynamically added inputs then? Currently, [attr.name]="<some expression>" is not considered correctly as a valid name but raises the name attribute error.

@StephenFluin
Copy link
Contributor Author

@vsavkin let me know that there may currently be an issue with submit events that made my use case fail in the first place. I was also missing ngNoForm.

In the new forms paradigm, for me to not use the new forms capabilities automatically, I can either not use a form element, or I can add <form ngNoForm>. This should work as I originally expected (no errors, and my forms still work). I'm testing further.

@mlittrell
Copy link

mlittrell commented Jun 20, 2016

@wrldtrvllr that's the exact case I'm dealing with. My dynamic form element (which renders a specific custom form element based on meta data from the API) breaks now in RC2 because it doesn't have a name, nor does it need one. In fact, in that case, name makes absolutely no sense.

@skejcikowski
Copy link

skejcikowski commented Jun 22, 2016

I can not use any custom-made form elements any more. For example, I am using a WYSIWYG-Editor in my forms:

<p-editor formControlName="description" [(ngModel)]="projectGroup.description"></p-editor>

Without the name I get this error: " Name attribute must be set if ngModel ..." . With the name set : "ORIGINAL EXCEPTION: No value accessor for 'description'" . I get the same for a custom-made DatePicker-Component.

The "new forms" are, like we call it in Germany, a classic "Verschlimmbesserung"

@zoechi
Copy link
Contributor

zoechi commented Jun 22, 2016

@skejcikowski You also needed a custom ControlValueAccessor previously to be able to use custom components with [(ngModel)].
The new forms are work in progress. Nobody said this won't be implemented otherwise the issue would be closed already.

@skejcikowski
Copy link

@zoechi yes I know this, and I'am using ControlValueAccessor's in my custom form components. But with the new forms it doesnt work anymore. Does anybody know when to expect the update ?

@skejcikowski
Copy link

@zoechi This is the appropriate channel - thats the Problem: "Name attribute must be set if ngModel ..." Not the control value accessor

@KiaraGrouwstra
Copy link
Contributor

Like @wrldtrvllr and @mlittrell, I've been using dynamically generated inputs.
As they stated, cases like that make names less sensible.

But this raises the question what use-case there is for forms that cannot serialize their own value.
I can think of two:

  • Inputs not used to submit, but just to manipulate values in the model. (@StephenFluin's use case seems like this, if a bad example -- his (submit) presumably still uses the input value through the model property, so Kara's approach might have worked there too.)
  • Inputs tied to Control structures defined in the model (e.g. <input [ngModel]="myControl">), meaning the Control can do the serialization instead of the inputs on the view.

Kara's "if you want ngModel to register with the parent form, the name attribute is required" still holds, but in these cases, you may not care about registering the inputs as part of a serializable form.

So I see two proposed alternatives:

  • ensure the <input> is not wrapped in a <form>
  • ensure the <form> the <input> is wrapped in has ngNoForm

The former suggestion surprises me, since not wrapping inputs in form elements would under some circumstances lead to the No provider for ControlContainer error.

Right now I've just added names on a few inputs to make the error go away, but it's a bit silly since the names/serialization are not actually used.
So I guess for me, Kara's option "have the form avoid registering any ngModels without names" would have sufficed.
But I do get the point -- defaulting to error messages to help new users with more common use-cases perhaps rightfully should take precedence over facilitating less common use-cases.

kara added a commit to kara/angular that referenced this issue Jun 23, 2016
@kara
Copy link
Contributor

kara commented Jun 23, 2016

Thank you all for the great feedback - very helpful! Here's how we are thinking about it:

In the case that you don't want to register a form control, you currently have a few options:

1 - Use ngModel outside the context of a form tag. This will never throw.

<input [(ngModel)]="person.food">         

2 - Use an ngNoForm attribute on the parent form tag to indicate you don't want Angular form support

<form ngNoForm>
   <input [(ngModel)]="person.food">         
</form>

Unfortunately, neither of these approaches are appropriate for the use case a few of you brought up where you might want some form controls registered in a form but not others. We should definitely support this case, so thanks for bringing it up. However, there are a few ways to approach this, and when considering a solution, dropping the error entirely is not ideal for two reasons:

1- It's not clear what's wrong if the name attribute is missing

We want to ensure a good developer experience for those starting out with forms. Having the form silently fail to pick up a form control's value and validation state in some cases is confusing. It appears that the forms module is simply broken. Throwing a friendly error is much more explicit, and provides immediate feedback to the developer about how to fix the form.

2- The "name" attribute becomes the gatekeeper for form registration

It seems arbitrary that the presence of a "name" attribute is what dictates the form control's relationship to its parent form. Currently, it's clear that the ngModel directive confers form functionality like validation state. If the "name" attribute controls the input's registration with its parent, then it muddies that concept and causes confusion about the relative effects of adding ngModel vs. a name (e.g. can you use the "name" attribute without ngModel, since it is responsible for form registration?)

Solution:
After considering the possibilities, we've decided to add an option to ngModelOptions to indicate a form control is "standalone". This allows developers to mark that certain controls shouldn't be registered with the parent form if necessary. In other words, within a form, an input needs either a name attribute or a standalone marker.

<form>
   <input [(ngModel)]="person.food" [ngModelOptions]="{standalone: true}">         
</form>

It is preferable to have developers explicitly and deliberately indicate that they'd like to override the default behavior, rather than making an assumption based on the absence of an attribute. Using ngModelOptions is a natural choice because it is a clear extension of ngModel (rather than a distinct attribute or directive).

This is a bit more boilerplate to write for these special controls, but it seems like this is an acceptable tradeoff given that this is the less common use case. This way, developers can still create special standalone controls, but the standard developer experience is not degraded.

See PR: #9522

kara added a commit to kara/angular that referenced this issue Jun 23, 2016
@kara kara closed this as completed in 6edf047 Jun 23, 2016
@about-code
Copy link

@kara Could you please rethink the name "standalone". It leaves me standing alone. From reading it I absolutely cannot infer what it means without the explanation you've given. Wouldn't be

<form>
   <input [(ngModel)]="person.food" [ngModelOptions]="{isModelProperty: false}">  <!-- Suggestion 1 -->
   <input [(ngModel)]="person.food" [ngModelOptions]="{isFormProperty: false}">  <!-- Suggestion 2 -->
   <input [(ngModel)]="person.food" [ngModelOptions]="{serialize: false}">  <!-- Suggestion 3-->
</form>

more in line with terminology used elsewhere for forms? You might notice that these properties use
inverse logic to standalone and "true" would be the default cases. I know, it seems tedious to have discussions about names etc. but many of the changes we have to deal with when porting forms from RC.1 are also because of the miserable naming chosen ealier. So please rethink if standalone is such a good fit.

@KiaraGrouwstra
Copy link
Contributor

In retrospect, I think my 'serialize' terminology wasn't so great either, since this seems more about getting the info into a JS object rather than making that object into a string.

@omerfarukyilmaz
Copy link

For anyone having trouble with the name attribute affecting other checkboxes in a loop, I am using this temporary workaround:

Create the for loop:
*ngFor="let model of modelList let i=index"

Give it a unique name based on index:
<input type="checkbox" [(ngModel)]="model.sendMail" name="sendMail{{i}}">

Interpolation seems to do the trick.

@bibhas2
Copy link

bibhas2 commented Jul 11, 2016

The problem is that your forms will no longer behave predictably. In the example above, what would form.value be? There's no way to refer to the input you've added, so it would have to be {undefined: 'someval'}

If name is absent for an input it should simply not be serialized in the form's JSON.

Serializing to JSON should be an opt in approach (by providing name) not an opt out using ngModelOptions. In most cases I need to construct my own JSON from the input fields to conform to a web service's request. A form's JSON is not terribly useful in these cases.

Having to specify name at all times is simply bad ergonomics for the API.

@vuthantps
Copy link

I discovered that if the form tag has a ngIf directive the error will vanish. So if you want to use form control validation and binding to value to model you could have something like:

<form [formGroup]="form" *ngIf="true">
   <input [(ngModel)]="person.name" [formControl]="form.controls['name']">         
</form>

@KiaraGrouwstra
Copy link
Contributor

Uh-oh, that's definitely shorter, but now it might be considered a bug instead of a feature and get fixed. :P

@e-oz
Copy link

e-oz commented Aug 19, 2016

All changes to the Forms API were good, except this one, the most ugly change. Still don't understand why option 'standalone' can't be set by default, when input element don't have a name.
Adding more restrictions to the API will not make it user-friendly or easy to use.

@KiaraGrouwstra
Copy link
Contributor

@e-oz @kara: Idea: what if we could get an application/module-wide boolean setting injected by DI to toggle this default? That way perhaps it might be possible to satisfy both new and experienced users?
I mean, it'd beat the point if everyone would switch to it and that way new users would still start complaining about not understanding why their inputs aren't included in form submission data, but yeah. It might make sense.

@e-oz
Copy link

e-oz commented Aug 19, 2016

I have better idea: we can use string value of the[(ngModel)] attribute as a name. Taking code from @kara example, name would be person.food. Or person_food if dot is not allowed by some reasons.

@KiaraGrouwstra
Copy link
Contributor

Hm, kinda on the fence about it, implementation considerations aside. That said, technically the ideas aren't mutually exclusive; I suppose any alternative settings could just be another option. We don't need to change the default just to be able to add other options.

@gatuteck
Copy link

Hello kara in your next tutorial please explain more about
how we can access values of multiple checkbox /radio buttons when we hit submit button. Sorry I am new to angular 2.

@figloalds
Copy link

figloalds commented Apr 16, 2017

Ok I believe the name thing in forms is for Testability, so that the function in the component receives an object instead of simply modeling into a public object in class (easier to write tests), I forgot you nerds like that unit test thing.
Then again, if I'm using name/value from the form itself, then using ngModel makes no sense, or if I'm using ngModel (thus directly binding inputs to public variables in component's scope), then the names from the form are irrelevant, aren't they?
I don't know if I'm correct, but if so, then the default for ngModel should be to not require a name at all.
I mean, by one hand you can have a public model in your component and alter it using ngModels throughtout a set of inputs, this process doesn't rely on angular 2 form-to-object thingy and was done way back in angular 1 with 0 hassle.
Or you can have a form that when submitted does function with angular generated object based on name/value, which doesnt rely on ngModel (more testability, ng2 style)
Nevertheless, i see 0 connection between those.

@figloalds
Copy link

Ok, I've taught myself a bit more of ng2 forms and it appears as the name is used solely for naming a in-form field that angular can get to retrieve validation data, it should be optional, as the user might not need validation, and it shouldn't be necessary to specify ngNoForm or ngModelOptions standalone to true, angular could detect its a standalone by checking if name is undefined

@KiaraGrouwstra
Copy link
Contributor

@felyperennan you should read Kara's responses here to get a sense of their considerations.

@figloalds
Copy link

Yeah, I've been reading this through, I understand the reasons and I'll be using ngNoForm or ngOptions adequately, it was never necessary in angularjs 1 and it used to "just work". id be looking for a more global configuration where ngforms behavior could be tweaked, but that's far fetched and I understand now. Thanks.

@KiaraGrouwstra
Copy link
Contributor

@felyperennan: If it's any consolence, I'd personally managed to work around having to manually specify this extra attribute by abstracting the check away into a Pug mixin. But yeah, then you might need to use that.

@nagenderRawat
Copy link

nagenderRawat commented Jan 11, 2018

Hi,
Even I have used [(ngModel)] along with formControlName, and that's because I have a large complex form having similar type of input elements(400+) having same type of validations.
Those input elements contains the values of large JSON object so I wanted a solution where I should not have to touch my main object, so I just some how iterated the main object to build the form and if any value of that object is changed(through change in its corresponding input field) it will automatically reflect in component.
Therefore when we change any value on form input field then its value is updated in main object in component too due to (ngModel). and I send updated object on form submit to destination.
But at the same time I wanted to use form validation power here so I have to create form controls for all input fields and used formControlName along with ngModel to acheive form validation as well as data auto change on main object.
Can any one suggest, is it a correct approach in angular 4 or not ?

@kapilSoni101
Copy link

@kara sir,i have tried both your solution but still got error i am using [ngModelOptions]="{standalone: true}" on please tell me sir how to fix this error?

@mehdibenhassen2
Copy link

mehdibenhassen2 commented Apr 12, 2019

use ngNoForm in the form tag and also [ngModelOptions]="{standalone: true}"
<form ngNoForm >
<input [(ngModel)]="chooseName" [ngModelOptions]="{standalone: true}"
</form>

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: forms feature Issue that requests a new feature
Projects
None yet
Development

No branches or pull requests