使用部分视图时,MVC模型在帖子上为空

时间:2014-02-05 10:42:54

标签: c# asp.net-mvc asp.net-mvc-4

我有一个MVC控制器,其中post方法上的模型总是返回null。我不确定这是因为我在表单中使用了部分视图。

知道模型没有返回控制器的原因吗?

模型

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

enter image description here

3 个答案:

答案 0 :(得分:13)

正如您所正确指出的那样,这是因为您使用了部分内容。发生这种情况是因为Html.Partial不知道它正在对集合进行操作,因此它不会为您的表单元素生成名称,而是为了绑定到集合。

但是,您案例中的修复似乎相当简单。您只需将部分更改为Html.Partial,然后在该模板上调用EditorTemplate,而不是使用Html.EditorForHtml.EditorFor非常聪明,可以知道它何时处理集合,因此它将为集合中的每个项调用模板,在表单上生成正确的名称。

为了做你需要的,请按照以下步骤操作:

  1. 在视图的当前文件夹中创建一个EditorTemplates文件夹(例如,如果您的视图为Home\Index.cshtml,请创建文件夹Home\EditorTemplates)。该名称很重要,因为它遵循查找模板的惯例。
  2. 将部分视图放在该文件夹中。或者,将其放在Shared\EditorTemplates文件夹中。
  3. 将部分视图重命名为Datapoint.cshtml(这很重要,因为模板名称基于类型名称的约定)。
  4. 现在相关的视图代码变为:

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

    这将正确生成名称,并应解决模型绑定问题。

    OP的附录

    按照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 - 清洁解决方案

    我对每个EditorTemplate使用DataPoint的建议也适用于每个Group。您可以通过为for设置EditorTemplate,而不是需要Group循环,再次在视图中显示逻辑。在放置模板的位置方面,上述步骤同样适用。

    在这种情况下,模板将为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();
    }
    

答案 1 :(得分:1)

如果像这样返回,Binder无法绑定到对象列表。是的,部分是你的问题。您需要在表单中为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's blog

中列表的详细信息

答案 2 :(得分:0)

由于模型绑定器处理集合的方式,您将获得空模型。

您的局部视图正在渲染这些输入,例如:

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