If the model is a list of objects then @Html.LabelFor model => model[i].member)
creates an empty for
attribute.
The DisplayName works properly, model binding also works just fine. The only thing that does not work is the for
attribute.
Below there are two versions of the MVC code. The first 3 code snippets are for the model, controller and view combination that does not work, (the model is a list). The second set of 3 code snippets work. This time the list is wrapped in an object.
What am I missing?
Model:
public class ViewModel {
public bool ClickMe { get; set; }
}
Controller:
public ActionResult LabelForViewModel() {
List<ViewModel> model = new List<ViewModel>() {
new ViewModel() { ClickMe = true}
};
return View(model);
}
View:
@model List<ModelBinding.Models.ViewModel>
// form code removed for brevity
@for (var i = 0; i < Model.Count; i++) {
<div class="form-group">
@Html.LabelFor(model => model[i].ClickMe)
<div class="col-md-10">
@Html.EditorFor(model => model[i].ClickMe, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model[i].ClickMe, "", new { @class = "text-danger" })
</div>
</div>
}
Markup:
<div class="form-group">
<label for="">ClickMe</label>
<div class="col-md-10">
<input name="[0].ClickMe" class="form-control check-box" type="checkbox" checked="checked" value="true" data-val-required="The ClickMe field is required." data-val="true"><input name="[0].ClickMe" type="hidden" value="false">
<span class="field-validation-valid text-danger" data-valmsg-replace="true" data-valmsg-for="[0].ClickMe"></span>
</div>
</div>
Please notice that the for field is empty and the input has no id field (that one is used in the "for" field).
I have created a wrapper model that contains a list of objects, modified the controller and view and then it works just fine. In this case the "for" field is filled in correctly and everything works as expected.
public class ListWrapper {
public List<ViewModel> ViewModels { get; set; }
}
View:
@for (var i = 0; i < Model.ViewModels.Count; i++) {
<div class="form-group">
@Html.LabelFor(model => model.ViewModels[i].ClickMe)
<div class="col-md-10">
@Html.EditorFor(model => model.ViewModels[i].ClickMe, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ViewModels[i].ClickMe, "", new { @class = "text-danger" })
</div>
</div>
}
Markup:
<div class="form-group">
<label for="ViewModels_0__ClickMe">ClickMe</label>
<div class="col-md-10">
<input name="ViewModels[0].ClickMe" class="form-control check-box" id="ViewModels_0__ClickMe" type="checkbox" checked="checked" value="true" data-val-required="The ClickMe field is required." data-val="true"><input name="ViewModels[0].ClickMe" type="hidden" value="false">
<span class="field-validation-valid text-danger" data-valmsg-replace="true" data-valmsg-for="ViewModels[0].ClickMe"></span>
</div>
</div>
So, it seems that the for
attribute is empty only if the model is a list of objects.
Is this a known issue? Or maybe I'm missing something?
The @model declaration assigns the model object to the
Model
variable (note the captial 'M'). But then you are reading frommodel
, which hasn't been assigned.Try this:
That doesn't explain why your second example works at all, but hey, give this a try.
This behavior is the result of the framework complying with HTML-4 specifications. In HTML-4
When your model is a collection and you use
@Html.EditorFor(m => m[i].SomeProperty)
to generate form controls, the method generatesname
andid
attributes based on the name of the property. In this case thename
attribute for the first item in the collection will bebut to avoid clashes with jQuery selectors, the method replaces
.
,[
, and]
characters with an underscore_
when generating theid
attribute which would result inbut because this is invalid according to the HTML-4 specification, the helper does not output it in the html.
The same applies when using
@Html.LabelFor()
(since the associated form control will not have anid
attribute, the value of thefor
attribute is not output in the html.Note that in HTML-5, the
id
attribute would be valid, so hopefully this behavior will be dropped in the future.Your second example works because
id="ViewModels_0__ClickMe
" begins with a letter and is therefore valid.Side note: You could solve the first issue by generating your own
id
attributes, for example