Reputation: 46479
When does the expression passed to DisplayFor
and EditorFor
need to use the model to do its data access?
For example, I might have the following Models
class MyModel {
IList<SubModel> Subs { get; set; }
}
class SubModel {
string Name { get; set; }
}
I could then write my view data access using a full path starting at the model:
@Html.DisplayFor(m => m.Subs[i].Name)
Or, I might have something like this within the view that doesn't use the model:
@foreach (var item in Model.Subs) {
@Html.DisplayFor(m => item.Name)
}
But in the above I'm not actually using the model. This seems to have worked every time I've tried, but under what conditions might I run into problems?
EditorFor
typically uses the Expression
to build the name/id into the form elements. Is it ever safe to not use the model when specifying an expression passed to EditorFor
?
Upvotes: 4
Views: 421
Reputation: 5193
Brief answer is Yes - there has to be a model to pass as the argument to the lambda method. If your View does not have a model and you call DisplayFor or EditorFor, you will see the error message, "Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions."
So you have to have a model in order to use DisplayFor but you don't actually have to use it. For example, you could do this:
Html.EditorFor(m => i)
In that case, the Html name and id will both be "i".
But there are some considerations to be aware of. You might want to create a strongly-typed partial view for SubModel for example, in your Shared/Display Templates or Editor Templates folder. In which case you can use a foreach with DisplayFor.
If you are using EditorFor and you want the posted fields to bind back to the model, you will need to use:
for (i = 0; ...)
rather than foreach so that your Html form fields end up with bindable names such as Subs[1].Name. If you use foreach, all your inputs will have the same name and id, eg:
id="s_Name" name="s.Name"
whereas with a for loop, you get:
id="SubModels_0__Name" name="SubModels[0].Name"
The for loop produces legal html (unique ids) and can be rebound to a list on the server.
To clarify: when you use EditorFor or DisplayFor there are two things to be aware of. Firstly, the expression you pass in determines the field name used for the Html element. If the expression relates to your model, the name is derive from the model, as shown in the example above. So if I bound to a subclass on the model:
Html.DisplayFor(m => m.Submodel.Name)
the Html field name will be 'Submodel.Name' and it will rebind on postback to the same hierarchy (note that you can also set the name yourself using one of the overloaded methods).
The second aspect is that your expression is typed (that is, it resolves to a CLR type or one of your custom types). In order to render that type, MVC looks for a template whose model matches. It looks for its templates using a path hierarchy starting with the current View folder, then the Shared folder, and it falls back to its internal templates if a custom one cannot be found.
So you need to consider both of these to achieve the rendering and postback binding you want. But if you are only interested in displaying the data, you don't have to worry about your Html field names matching your model and you can use any arbitrary names or none at all and let MVC generate them. But you should also be aware that Html ids should be unique and as in the foreach example above, you can end up with the same id. The current crop of browsers don't seem to have a problem with that but if you want to use Javascript to select by id, it would be interesting.
Upvotes: 1