5

我有一个视图,用户可以通过选择复选框来选择任意数量的俱乐部。俱乐部是主模型的属性,类型为 List <ClubModel>。在重构时,我从这个开始:

@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>
}

这很好用:模型返回一个填充的 Clubs 属性。

现在我取出 <td> 标签并将它们移动到 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>
}

这仍然有效(模板未显示)。

现在我也想将循环移动到 EditorTemplate:

@using (Html.BeginForm())
{
    <fieldset>
        <legend> Select Clubs</legend><br />
        <EditorFor(model=>model.Clubs,"ListOfClubs")
         <input type="submit" value="Submit" />
    </fieldset>
}

我正式创建了一个名为“ListOfClubs”的 EditorTemplate:

@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>

这正确显示了带有 IsAvailable 属性复选框的俱乐部,但现在发布模型的 Clubs 属性为空!

我哪里错了?

编辑:我试图通过使用来实现 Cymen 的答案:

@Html.EditorFor(model=>model.Clubs,"ClubModel") 

或者在传入这些元素的列表时指定元素模板。然而,我遇到了一个异常:System.InvalidOperationException 未被用户代码处理 Message=传递到字典中的模型项的类型为“System.Collections.Generic.List`1[InvallersManagementMVC3.ViewModels.ClubModel]”,但是这本字典需要“InvallersManagementMVC3.ViewModels.ClubModel”类型的模型项。

4

2 回答 2

7

您似乎正在尝试将传递给视图的模型列表按 3 分组。因此,为了重构您的代码,我建议您首先引入一个正确的视图模型 => 一个反映此特定视图要求的视图模型:

public class GroupedClubs
{
    public IEnumerable<StandInClubModel> Clubs { get; set; }
}

现在在控制器动作中,我们应该简单地将域模型转换为这个视图模型的列表:

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);
}

现在剩下的就是写一些视图:

~/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>

这几乎就是全部了。现在你可以有一个控制器动作来处理表单提交:

[HttpPost]
public ActionResult Index(List<GroupedClubs> clubs)
{
    ... map the view model back to some domain model and pass
        to the service layer for processing
}
于 2011-08-21T15:20:07.923 回答
3

为 ClubModel 的一个实例创建一个 Editor,并让 ASP.NET MVC 呈现它(让它进行迭代)。ASP.NET MVC 为输入标签提供了一些特定的命名/ID 方案,并且您不会在迭代中呈现它们。

所以使用这个——和你的一样,但要注意模板名称:

@using (Html.BeginForm())
{
    <fieldset>
        <legend>Select Clubs</legend><br />
        <table>
            <%: EditorFor(model=>model.Clubs, "Club") %>
        </table>
        <input type="submit" value="Submit" />
    </fieldset>
}

和编辑器。请注意,即使您传入了上面的列表,它也适用于模型的单个实例。这是 ASP.NET MVC 的“魔法”。

<tr>
    <td>
        @Html.HiddenFor(model => model.ClubID)
        @Html.EditorFor(model => model.IsAvailable)
    </td>
    <td>@Html.DisplayFor(model => model.ClubName)</td>
</tr>
于 2011-08-21T02:25:13.707 回答