Asp.Net MVC Binding. Get List<Model> at controller
I've got view:
@using (Html.BeginForm("ProcessRoute", "Calculator"))
{
@Html.TextBox("CityArrival", null, new { @style = "width: 300px;" })
@Html.TextBox("CityDeparture", null, new { @style = "width: 300px;" })
@{ Html.RenderPartial("BusModelList", @Model.BusModels); }
<input id="submit" type="submit" value="Поиск" />
}
partial view:
@model List<ViewModels.GroupedBusModelsSelectable>
<table>
@foreach (var item in Model)
{
<tr>
<td colspan="3" align="left">@item.BusType.Name</td>
</tr>
foreach (var bus in item.BusModels.OrderBy(c => c.Bus.NumPlaces).ToList())
{
@Html.EditorFor(x=>bus);
}
}
</table>
and editor:
@model ViewModels.BusModelSelectable
<tr>
<td>
@Html.HiddenFor(x => x.Bus.Id)
@Html.HiddenFor(x => x.Bus.FullName)
@Html.CheckBoxFor(x => x.IsSelected)
</td>
<td>
<img>
<td>
@Model.Bus.FullName
</td>
</tr>
Now I would like to get all busModels(with selected) at controller:
[HttpPost]
public ActionResult ProcessRoute(string CityArrival, string CityDeparture, IEnumerable<GroupedBusModelsSelectable> BusModels)
By some reason BusModels is null
At HttpContext.Request.Form
I see list of (bus.IsSelected, bus.Bus.Id). How can I get it at controller?
DECISION
After Nick Larsen advice I understand that I should have indexed names at my html. Because I have two-level collection, I should do it twice.
PartialView:
@model List<Avtobus66.ViewModels.GroupedBusModelsSelectable>
<table>
@for (int j = 0; j < Model.Count(); j++)
{
<tr>
<td colspan="3" align="left">@Model[j].BusType.Name</td>
</tr>
for (int i = 0; i < Model[j].BusModels.Count(); i++)
{
@Html.EditorFor(x => Model[j].BusModels[i]);
}
}
</table>
Let's see html now:
<input name="[0].BusModels[0].Bus.Id" type="hidden" value="1" />
<input name="[0].BusModels[0].IsSelected" type="checkbox" value="true" />
<input name="[0].BusModels[0].IsSelected" type="hidden" value="false" />
<input name="[0].BusModels[1].Bus.Id" type="hidden" value="2" />
<input name="[0].BusModels[1].IsSelected" type="checkbox" value="true" />
<input name="[0].BusModels[1].IsSelected" type="hidden" value="false" />
<input name="[0].BusModels[2].Bus.Id" type="hidden" value="3" />
<input name="[0].BusModels[2].IsSelected" type="checkbox" value="true" />
<input name="[0].BusModels[2].IsSelected" type="hidden" value="false" />
<input name="[1].BusModels[0].Bus.Id" type="hidden" value="4" />
<input name="[1].BusModels[0].IsSelected" type="checkbox" value="true" />
<input name="[1].BusModels[0].IsSelected" type="hidden" value="false" />
At controller I'm ready get 2level collection now. At my case
IEnumerable<GroupedBusModelsSelectable> BusModels
it works. Now I'm agree with N开发者_如何学Cick, I should refactoring my table markup spaghetti.
You need to look at what you are sending to the browser. In order for the BusModels
parameter to be populated, you need to send to the browser something like:
<input type="hidden" name="BusModels[0].someProperty" value="whatever" />
<input type="hidden" name="BusModels[0].someOtherProperty" value="whatever" />
<input type="checkbox" name="BusModels[0].someBoolean" />
<input type="hidden" name="BusModels[1].someProperty" value="whatever" />
<input type="hidden" name="BusModels[1].someOtherProperty" value="whatever" />
<input type="checkbox" name="BusModels[1].someBoolean" />
Notice that the only thing that changed in the name is the value inside of the square brackets. There are other ways to do this, but they are all variants on this method and you can find out more about them here. Since you list no controls to dynamically add new elements, I suggest the rewriting the editor as a template and using the built in functionality to build your inputs.
To achieve this, pass your model to the partial view already sorted, then instead of using a foreach
, use a plain for
loop and call an editor for indexes of your model.
View:
@using (Html.BeginForm("ProcessRoute", "Calculator"))
{
@Html.TextBox("CityArrival", null, new { @style = "width: 300px;" })
@Html.TextBox("CityDeparture", null, new { @style = "width: 300px;" })
@{ Html.RenderPartial("BusModelList", Model.BusModels.OrderBy(c => c.Bus.NumPlaces).ToList()); }
<input id="submit" type="submit" value="Поиск" />
}
Partial View:
@model List<ViewModels.GroupedBusModelsSelectable>
<table>
@foreach (var item in Model)
{
<tr>
<td colspan="3" align="left">@item.BusType.Name</td>
</tr>
for (int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(x => x[i]);
}
}
</table>
That should do the trick. The only thing other than this that I would mention is that you have split up the table and the rows of the table which creates a tight coupling between the view and the partial view. A better way to handle this is to remove the coupling and put all related constructs in the same place. The easiest and cleanest way to do this is to use a razor inline template, or move away from the table altogether because semantically editors are not tabular data.
精彩评论