EditorTemplate for List of complex type returns null, while EditorTemplate for its elements works fine

给你一囗甜甜゛ 提交于 2019-12-04 12:12:55
Darin Dimitrov

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
}

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>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!