问题
I have a View in which the user can choose any number of Clubs by selecting checkboxex. The Clubs are a property of the main model with type List<ClubModel
>.
While refactoring I start out with this:
@using (Html.BeginForm())
{
<fieldset>
<legend>Voor Select clubs </legend><br />
<table>
<tr>
@for (var i = 0; i < Model.Clubs.Count; i++)
{
if (i % 3 == 0)
{
@:</tr><tr>
}
<td>
@Html.HiddenFor(model => model.Clubs[i].ClubID)
@Html.EditorFor(model => model.Clubs[i].IsAvailable)
</td>
<td>@Html.DisplayFor(model => model.Clubs[i].ClubName)</td>
}
</tr>
</table>
<input type="submit" value="Submit" />
</fieldset>
}
This works fine: the model is returned with a populated Clubs property.
Now I take out the <td
> tags and move them to an EditorTemplate:
@using (Html.BeginForm())
{
<fieldset>
<legend>Select Clubs </legend><br />
<table>
<tr>
@for (var i = 0; i < Model.Clubs.Count; i++)
{
if (i % 3 == 0)
{
@:</tr><tr>
}
@Html.EditorFor(model=>model.Clubs[i])
}
</tr>
</table>
<input type="submit" value="Submit" />
</fieldset>
}
This still works (template not shown).
Now I want to move the loop too to an EditorTemplate:
@using (Html.BeginForm())
{
<fieldset>
<legend> Select Clubs</legend><br />
<EditorFor(model=>model.Clubs,"ListOfClubs")
<input type="submit" value="Submit" />
</fieldset>
}
I duly create a EditorTemplate named 'ListOfClubs':
@using InvallersManagementMVC3.ViewModels;
@model List<StandInClubModel>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<table>
<tr>
@for (var i = 0; i < Model.Count; i++)
{
if (i % 3 == 0)
{
@:</tr><tr>
}
<td>
@Html.HiddenFor(model => model[i].ClubID)
@Html.EditorFor(model => model[i].IsAvailable)
</td>
<td>@Html.DisplayFor(model => model[i].ClubName)</td>
}
</tr>
</table>
</body>
</html>
This correctly shows the clubs with checkboxes for the IsAvailable property, but now on posting the Clubs property of the model is null!
Where am I going wrong?
EDIT: I tried to implement Cymen's answer by using:
@Html.EditorFor(model=>model.Clubs,"ClubModel")
or specifying the elementtemplate while passing in a list of these element. However I am greeted by an exception: System.InvalidOperationException was unhandled by user code Message=The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[InvallersManagementMVC3.ViewModels.ClubModel]', but this dictionary requires a model item of type 'InvallersManagementMVC3.ViewModels.ClubModel'.
回答1:
You seem to be trying to group the model list passed to the view by 3. So in order to refactor your code I would recommend you start by introducing a proper view model => one that reflects the requirements of this specific view:
public class GroupedClubs
{
public IEnumerable<StandInClubModel> Clubs { get; set; }
}
Now inside the controller action we should simply convert the domain model into a list of this view model:
public ActionResult Index()
{
// This is our domain model. In a real world application
// it would come from a service layer. I am hardcoding some
// values here for simplicity
var clubs = Enumerable.Range(1, 8).Select(x => new StandInClubModel
{
ClubID = x,
ClubName = "club " + x
});
// Now we group the list of clubs by 3 in order to simplify
// our view code and avoid writing some ugly loops and spaghetti code
// In a real world application I would recommend externalizing this mapping
// between the domain model and the view model into a separate mapping layer
// AutoMapper is great for this job
var viewModel = clubs
.Select((club, index) => new { club, index })
.GroupBy(g => g.index / 3, i => i.club)
.Select(x => new GroupedClubs
{
Clubs = x
});
return View(viewModel);
}
Now all that's left is to write some views:
~/Views/Home/Index.cshtml
:
@model IEnumerable<GroupedClubs>
@using (Html.BeginForm())
{
<fieldset>
<legend> Select Clubs</legend>
<br />
<table>
<tbody>
@Html.EditorForModel()
</tbody>
</table>
<input type="submit" value="Submit" />
</fieldset>
}
~/Views/Home/EditorTemplates/GroupedClubs.cshtml
:
@model GroupedClubs
<tr>
@Html.EditorFor(x => x.Clubs)
</tr>
~/Views/Home/EditorTemplates/StandInClubModel.cshtml
:
@model StandInClubModel
<td>
@Html.HiddenFor(x => x.ClubID)
@Html.EditorFor(x => x.IsAvailable)
</td>
<td>
@Html.DisplayFor(x => x.ClubName)
</td>
and that's pretty much all. Now you could have a controller action which would handle the form submission:
[HttpPost]
public ActionResult Index(List<GroupedClubs> clubs)
{
... map the view model back to some domain model and pass
to the service layer for processing
}
回答2:
Make a EditorFor one instance of ClubModel and let ASP.NET MVC render it (let it do the iteration). ASP.NET MVC has some specific naming/id schemes for the input tags and you're not rendering them in your iteration.
So use this -- same as yours but observe the template name:
@using (Html.BeginForm())
{
<fieldset>
<legend>Select Clubs</legend><br />
<table>
<%: EditorFor(model=>model.Clubs, "Club") %>
</table>
<input type="submit" value="Submit" />
</fieldset>
}
And the EditorFor. Note that it is for a single instance of the model even though you're passing in a list above. This is ASP.NET MVC "magic".
<tr>
<td>
@Html.HiddenFor(model => model.ClubID)
@Html.EditorFor(model => model.IsAvailable)
</td>
<td>@Html.DisplayFor(model => model.ClubName)</td>
</tr>
来源:https://stackoverflow.com/questions/7135615/editortemplate-for-list-of-complex-type-returns-null-while-editortemplate-for-i