MVC模型在使用部分视图时提交空值问题

8

我有一个MVC控制器,在post方法中,模型始终返回为空。我不确定这是否是因为我在表单中使用了部分视图。

有什么想法,为什么模型没有被返回到控制器?

模型

enter image description here

加载模型

public List<Group> GetStaticMeasures(int businessUnitID)
{
    List<Group> groups = ctx.Groups
                           .Include("Datapoints")
                           .Where(w => w.BusinessUnitID.Equals(businessUnitID))
                           .OrderBy(o => o.SortOrder).ToList();

    groups.ForEach(g => g.Datapoints = g.Datapoints.OrderBy(d => d.SortOrder).ToList());

    return groups;
}

控制器

public ActionResult Data()
{
    ViewBag.Notification = string.Empty;

    if (User.IsInRole(@"xxx\yyyyyy"))
    {
        List<Group> dataGroups = ctx.GetStaticMeasures(10);
        return View(dataGroups);
    }
    else
    {
        throw new HttpException(403, "You do not have access to the data.");
    }
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Data(List<Group> model)
{
    ViewBag.Notification = string.Empty;

    if (User.IsInRole(@"xxx\yyyyyy"))
    {
        if (ModelState.IsValid)
        {
            ctx.SaveChanges(model);
            ViewBag.Notification = "Save Successful";
        }
    }
    else
    {
        throw new HttpException(403, "You do not have access to save the data.");
    }

    return View(model);
}

主视图

@model List<Jmp.StaticMeasures.Models.Group>

<div class="row">
    @using (Html.BeginForm())
    { 
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)     

        <div class="large-12">
            <div class="large-8 large-centered columns panel">
                @foreach (var g in @Model)
                { 
                    <h2>@g.Name</h2>
                    foreach (var d in g.Datapoints)
                    { 
                        @Html.Partial("Measures", d)                                 
                    }
                    <hr />
                }   

                <input type="submit" class="button" value="Save Changes"/>

            </div>
        </div>    
    }
</div>

局部视图

@model Jmp.StaticMeasures.Models.Datapoint

@Html.HiddenFor(d => d.ID)
@Html.HiddenFor(d => d.Name) 
@Html.HiddenFor(d => d.SortOrder)

@Html.DisplayTextFor(d => d.Name)
@Html.EditorFor(d => d.StaticValue)   
@Html.ValidationMessageFor(d => d.StaticValue)                      

显示连续ID的渲染HTML

输入图像描述在此

3个回答

15
正如你所指出的那样,这是因为你正在使用部分视图。这是因为 Html.Partial 不知道它正在处理一个集合,所以它不会为表单元素生成正确的名称以便绑定到集合。
但在你的情况下,解决方法似乎很简单。你可以将部分视图更改为 EditorTemplate,然后调用 Html.EditorForHtml.EditorFor 能够智能地识别是否处理集合,因此它将为集合中的每个项调用你的模板,生成正确的表单名称。
因此,按照以下步骤操作:
  1. 在当前视图文件夹内创建一个名为 EditorTemplates 的文件夹(例如,如果你的视图是 Home\Index.cshtml,则创建文件夹 Home\EditorTemplates)。这个名称很重要,因为它遵循查找模板的约定。
  2. 将你的部分视图放在该文件夹中。或者,将其放在 Shared\EditorTemplates 文件夹中。
  3. 将你的部分视图重命名为 Datapoint.cshtml(这很重要,因为模板名称基于类型名称的约定)。
现在相关的视图代码变成了:
// Note:  I removed @ from Model here.
@foreach (var g in Model)
{ 
    <h2>@g.Name</h2>
    @Html.EditorFor(m => g.DataPoints)
    <hr />
}

这可以确保您最初打算的视图分离。

根据评论更新

好的,正如我下面提到的,现在的问题是模型绑定程序无法将 DataPoint 与正确的 Group 关联起来。简单的解决方法是将视图代码更改为以下内容:
for (int i = 0; i < Model.Count; i++)
{ 
    <h2>@Model[i].Name</h2>
    @Html.EditorFor(m => m[i].DataPoints)
    <hr />
}

这样可以正确生成名称,并且应该解决模型绑定问题。

楼主的补充说明

在John的回答后,我还将Group表上缺失的属性作为HiddenFor包含进去,这使得我在提交后得到了模型。

@for (int i = 0; i < Model.Count(); i++)
{ 
    @Html.HiddenFor(t => Model[i].ID)
    @Html.HiddenFor(t => Model[i].BusinessUnitID)
    @Html.HiddenFor(t => Model[i].SortOrder)
    @Html.HiddenFor(t => Model[i].Name)

    <h2>@Model[i].Name</h2>
    @Html.EditorFor(m => Model[i].Datapoints)                                 
    <hr />                    
}

更新2 - 更简洁的解决方案

对于每个 DataPoint,我建议使用一个 EditorTemplate,同样适用于每个 Group。不需要 for 循环来在视图中添加逻辑,您可以通过为 Group 设置一个 EditorTemplate 来完全避免这种情况。放置模板的位置与上述步骤相同。

在这种情况下,模板名称应为 Group.cshtml,内容如下:

@model Jmp.StaticMeasures.Models.Group

<h2>@Model.Name</h2>
@Html.EditorFor(m => m.DataPoints)
<hr />

如上所述,这将对集合中的每个项目调用模板,同时为每个Group生成正确的索引。现在您的原始视图可以简化为:

@model List<Jmp.StaticMeasures.Models.Group>

@using (Html.BeginForm())
{
    // Other markup
    @Html.EditorForModel();
}

感谢您指向EditorTemplates。我已经更改了我的实现并进行了测试,但它仍然无法在post时返回模型。 - Phil Murray
@PhilMurray 你把我在便条中提到的 @ 符号也去掉了吗?那里不应该有它。另外,请检查生成的 HTML,确保您可以在输入元素上看到连续的索引。如果 DataPoint ID 不是连续的,则需要生成额外的 Html.Hidden 字段,如 此文章 中的“非连续索引”部分所述。 - John H
是的,我已经在 Model 上移除了 @。我已经在父 Group 表和子 Datapoint 表中更改了 ID,使它们连续,但在 post 操作中模型仍然返回 null。 - Phil Murray
@PhilMurray 正在更新我的答案,稍等一下。 - John H
1
@JohnH 感谢你的答案,我的朋友。你帮了我很多 =-) - Rafael Merlin
显示剩余7条评论

1
Binder无法绑定像这样返回的对象列表。是的,partial就是你的问题。你需要在表单中为ID指定一个编号。
做如下操作:
 // pseudocode
@model List<Jmp.StaticMeasures.Models.Group>

<div class="row">
    @using (Html.BeginForm())
    { 
        @Html.AntiForgeryToken()
        @Html.ValidationSummary(true)     

        <div class="large-12">
            <div class="large-8 large-centered columns panel">
                    for(int i; i<Model.Count; i++)
                    { 
                        <h2>@g.Name</h2>
                        @Html.HiddenFor(d => Model[i].Id)
                        @Html.HiddenFor(d => Model[i].Name) 
                        @Html.HiddenFor(d => Model[i].SortOrder)

                        @Html.DisplayTextFor(d => Model[i].Name)
                        @Html.EditorFor(d => Model[i].StaticValue)   
                        @Html.ValidationMessageFor(d => Model[i].StaticValue)  
                        <hr />
                    }

                <input type="submit" class="button" value="Save Changes"/>

            </div>
        </div>    
    }
</div>

请查看关于在Haack的博客中绑定列表的更多细节。


0
由于模型绑定程序处理集合的方式,您会得到一个null模型。
您的部分视图将这些输入呈现为例如:
<input type="hidden" name="ID" value="1"/>
...

然后对于您的List<Group>中的每个条目重复此操作。不幸的是,模型绑定程序不知道如何处理它,因此您将获得空值。

您的输入方式应如下:

<input type="hidden" name="groups[0].ID" value="1"/>
...
<input type="hidden" name="groups[1].ID" value="2"/>

编号不能中断。解决方法之一是重新编写使用Html.xxxFor方法的方式,例如:遍历列表并执行以下操作:

@Html.HiddenFor(d => Model[i].Id)

以下是两个资源,详细解释了如何使用模型绑定器处理集合,并提供了其他示例:

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接