繁体   English   中英

ViewModel没有在POST方法中传递回控制器

[英]ViewModel not being passed back to controller in POST method

简单的问题,但我无法弄清楚缺少什么。 我有一个简单的ViewModel(它会变得更大):

public class TigerTrackingViewModel
{
    public TigerTrackingViewModel()
    {
         this.TigerTrail = new TigerTrail();
    }
    public Guid YouthGuid { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public TigerTrail TigerTrail { get; set; }
}

TigerTrail是一个嵌套对象。 这是所有属性和子属性:

public class TigerTrail
{
    public TigerTrail()
    {
        DoneDate = new DateTime(1950, 01, 01);
        TigerTrailRequiredBadges = new Collection<TigerTrailRequiredBadge>();
        TigerTrailElectivedBadges = new Collection<TigerTrailElectiveBadge>();
    }
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<TigerTrailRequiredBadge> TigerTrailRequiredBadges { get; set; }
    public virtual ICollection<TigerTrailElectiveBadge> TigerTrailElectivedBadges { get; set; }
    //public virtual ICollection<Youth> Youth { get; set; }
    public bool? Done { get; set; }
    public DateTime? DoneDate { get; set; }
}

所以它有TigerTrailRequiredBadges:

public class TigerTrailRequiredBadge
{
    public TigerTrailRequiredBadge()
    {
        DoneDate = new DateTime(1950, 01, 01);
        TigerTrailRequiredBadgeSubRequirements = new Collection<TigerTrailRequiredBadgeSubRequirement>();
    }
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual ICollection<TigerTrailRequiredBadgeSubRequirement> TigerTrailRequiredBadgeSubRequirements { get; set; }
    public bool Done { get; set; }
    public DateTime DoneDate { get; set; }
}

并且有TigerTrailRequiredBadgeSubRequirement(s):

public class TigerTrailRequiredBadgeSubRequirement
{
    public TigerTrailRequiredBadgeSubRequirement()
    {
        DoneDate = new DateTime(1950, 01, 01);
    }
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public string ShortCode { get; set; }
    public string Type { get; set; } //Family, Den, Go See It
    public string Description { get; set; }
    public bool Done { get; set; }
    public DateTime DoneDate { get; set; }
}

回到TigerTrail.cs类,还有Elective Badge类:

public class TigerTrailElectiveBadge
{
    public TigerTrailElectiveBadge()
    {
        DoneDate = new DateTime(1950, 01, 01);
    }
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public int Number { get; set; }
    public string Name { get; set; }
    public string Requirement { get; set; }
    public bool Done { get; set; }
    public DateTime DoneDate { get; set; }
}

因此,我的ViewModel将提供所有可用的属性。 不幸的是,大部分都有。 它很大而且很难看,但我必须让它发挥作用。

在Controller GET方法中:

public ActionResult TigerTrail()
    {
        var vm = new List<TigerTrackingViewModel>();
        var pack = Ctx.CubPacks.FirstOrDefault(x => x.Id == PackId);
        var permTrail = Ctx.TigerTrails.FirstOrDefault(x => x.Name.Contains("PERM"));
        foreach (var youth in pack.Youths)
        {
            //if anyone does not have this trail set up, make a new one.
            if (youth.TigerTrail == null)
            {
                youth.TigerTrail = new TigerTrail();
                if (youth.TigerTrail.TigerTrailElectivedBadges == null)
                {
                    youth.TigerTrail.TigerTrailElectivedBadges = new Collection<TigerTrailElectiveBadge>();
                }
                if (youth.TigerTrail.TigerTrailRequiredBadges == null)
                {
                    youth.TigerTrail.TigerTrailRequiredBadges = new Collection<TigerTrailRequiredBadge>();
                }
                youth.TigerTrail = permTrail;
            }

            youth.TigerTrail.Name = youth.FirstName + " " + youth.LastName + " Tiger Trail";
            vm.Add(new TigerTrackingViewModel
            {
                FirstName = youth.FirstName,
                LastName = youth.LastName,
                YouthGuid = youth.YouthGuid,
                TigerTrail = youth.TigerTrail
            });
        }
        return View(vm);
    }

在post方法中:

[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
    return View();
}

每次回发都会返回null。 这是观点:

@model List<eTrail.Cubs.ViewModels.TigerTrackingViewModel>
@{
    ViewBag.Title = "Award Tracking";
    Layout = "~/Areas/App/Views/Shared/_BackendDashboard.cshtml";
}
<h1>Award Tracking</h1>
<hr />
<div class="row">
    <div class="span12">
        @using (Html.BeginForm("TigerTrail", "Awards", FormMethod.Post, new { @class = "form-horizontal" }))
        {
            for (int i = 0; i < Model.Count; i++)
        {
        @Html.HiddenFor(x => Model[i].TigerTrail)

            foreach (var item in Model[i].TigerTrail.TigerTrailElectivedBadges)
            {
        @Html.DisplayFor(x => item.Done)
        @Html.DisplayFor(x => item.DoneDate)
        @Html.DisplayFor(x => item.Id)
        @Html.DisplayFor(x => item.Name)
        @Html.DisplayFor(x => item.Number)
        @Html.DisplayFor(x => item.Requirement)
            }

        }      
        <input type="submit" value="submit" />
        }
</div>
</div>

我之所以添加了所有HiddenFor字段,是因为有人建议它们都必须存在才能发回。 仍然没有运气。 如果我在页面上查看源代码,则id / name将如下所示:

<li>
    <input id="elec_Done" name="elec.Done" type="checkbox" value="true"><input name="elec.Done" type="hidden" value="false">
    <b>Pet Care</b>
    Visit a veterinarian or animal groomer
    <input id="elec_Number" name="elec.Number" type="hidden" value="43">
    <input id="elec_DoneDate" name="elec.DoneDate" type="hidden" value="1/1/1950 12:00:00 AM">
</li>

翻译中迷失了什么? 如何将列表返回给控制器?

编辑

根据赏金以来的两个答案,我需要澄清一下:在httppost方法中,应该接收的列表为空。

[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths) //this is what is null on postback.
{
    . . . Do work with youths . . 
    return RedirectToAction(...);
}

我已经根据您的代码成功完成了这项工作。 HttpGet方法视图的修复形式如下:

@using (Html.BeginForm("TigerTrail", "Awards", FormMethod.Post, new { @class = "form-horizontal" }))
{
    for (int i = 0; i < Model.Count; i++)
    {
        @Html.EditorFor(x => x[i].FirstName)

        for (int j = 0; j < Model[i].TigerTrail.TigerTrailElectivedBadges.Count; ++j)
        {
            @Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Name)
        }
    }      
    <input type="submit" value="submit" />
}

然后,您可以取出已添加的大量集合初始化。 (顺便说一下,那里有一个错误 - 行youth.TigerTrail = permTrail;完全替换前面空的但初始化的对象。)

简短说明

诀窍是使用EditorForHiddenFor包含所需的所有字段,因为所有其他字段将为null / blank / default / empty。 这些天不需要为MVC模型绑定初始化集合, 如果有什么内容可以自动创建它们(即使用EditorForHiddenFor东西)。 DisplayFor 不会导致值往返 如果没有对象的值进行旅行,该对象将不在该集合中; 如果集合中没有项目进行往返,则除非您强制使用空集合,否则集合将不会在回发中存在。

语法也很关键:所有索引( foo[i].bar[j].baz )都必须位于您提供的lambda 即,在=>的右方括号之前) 如:

@Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Name)
                  //  ^^^                                     ^^^

要使其正常工作,您必须将ICollection<T>Collection<T>更改为IList<T>List<T>以便可以在标记中建立索引。

更长的解释

正如其他评论者提到的那样,除非您编写自己的模型绑定器,否则将从您的HttpGet方法,通过其视图的形式到HttpPost方法进行往返的唯一数据是:

  • 显式包含在表单字段中(因此HTML <input ...>标记是可见的还是隐藏的), 以及
  • 可以通过MVC标准类DefaultModelBinder理解,这意味着inputname属性必须是特定格式。

这意味着您必须坚持使用DefaultModelBinder可以使用的类型,并且要小心使用EditorForHiddenFor等,尤其是对于集合的集合。

因此,如果您希望任何TigerTrailViewModel的TigerTrail成员跨越它,您需要为每个成员指定EditorForHiddenFor 你不需要一个对象本身的隐藏字段(例如@Html.HiddenFor(x => x.TigerTrail) 。你需要在另一边想要查看其成员的字段。如果你想要列表要使用TigerTrails的选择性徽章,然后使用上面的语法,对于要查看的徽章的每个字段,必须确保这些字段是EditorForHiddenFor

作为脚注,只要类型匹配, TextBoxFor或任何其他xxxxFor方法也可以正常工作。

你为什么要把所有东西都放在lambda里面( =>右边)? 这取决于MVC对表达式树的使用,除非你非常感兴趣,否则可能不值得担心,但是:你在对EditorFor或类似的调用中提供的lambda变成了一个表达式树而不是一个委托,并且MVC解析这个编译器生成的数据结构在运行时计算出如何为它创建的输入写出name属性,以便以后可以回读。

如果您使用EditorFor如:

@Html.EditorFor(x => x[i].TigerTrail.TigerTrailElectivedBadges[j].Done)

然后MVC将创建输入1a:

<input class="check-box" name="[6].TigerTrail.TigerTrailElectivedBadges[28].Done" type="checkbox" value="true" />

并且该名称语法是MVC的DefaultModelBinder所需的语法。

顺便提一下,如果你真的想要,你可以用这些名称格式手动生成输入标签,而不是使用EditorFor等。 这对于用户将项目添加到集合的应用程序非常有用,如果您有JavaScript,则可以动态添加相关的表单字段。 如果名称格式正确,MVC将在回发时找到它们。

建议(FWIW,可能不多)

MVC与Web窗体不同,因此没有视图状态会自动保存页面上的每个项目供以后使用。 这是一件好事,因为它可以加速您的应用程序,从而加快加载速度并减少不必要的互联网流量。 通常,良好的做法是仅包含用户可能更改的数据,或者只需要了解用户提供的其他值(例如,唯一的记录ID,这将帮助您在数据库中找到用户的某些内容)正在努力)。

如果你想在回发上显示或使用其他数据,一般来说最好再次查找(基于用户提供的ID等或隐藏字段中包含的ID等),而不是在往返时不经意地传送它。

因此,如果您的嵌套集合没有在往返中返回,那么除非用户可以编辑该数据,否则无关紧要; 如果你真的需要它,你可以再次将它从数据库中拉出来。

对于某些应用程序(不是最常见的MVC应用程序),当然可以想象您可能想要传输对象的整个状态(例如,如果您无法更改数据库表的多用户不友好模式多个用户可能正在编辑,但您希望通过保存所有值,检查差异并拒绝编辑(如果它们发生冲突)来阻止他们覆盖彼此的工作。 在这种情况下,您将不得不往返传输所有数据,并且需要使用HiddenFor和正确的语法或您自己设计的另一种机制来进行处理。

看来您需要添加表单字段以绑定到Pack属性。 否则,将没有任何内容可以发回到控制器。 http是无状态的,因此不会保留原始模型对象,因此必须从表单字段中重建它

您需要的答案很简单,您不需要使用隐藏字段或其他内容。

public ActionResult TigerTrail()
{
    var vm = new List<TigerTrackingViewModel>();
    //other codes
    return View(vm) //Here is the important for you. Returning something inside View()is to show the Model and data.
}

您的另一个使用HttpPost的View在View()内部不返回任何内容;

[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
    return View(); //Here is where you did not give any data/model to the View. So, if you don't return something inside View(), you can not present any data or can not use model in the View.
}

你所要做的就是:

[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
    return View(youths); //It will work. 
}

需要帮助请叫我。

[HttpPost]
public ActionResult TigerTrail(List<TigerTrackingViewModel> youths)
{
    return View();
}

我假设您的列表不为空,所以您只是忘记将模型传递给视图。 像这样:

return View(youths);

如果您需要在隐藏字段中编写许多内容,请考虑每次从数据库获取模型并仅使用隐藏字段ID。

您可以更改一些内容:

1)在ViewModel中,最好在构造函数中初始化复杂属性。 收藏品特别适用。 不知何故,模型绑定器似乎无法做到这一点。 这以Null属性结束。 我认为这不是您的情况 ,因为您的整个VM列表都为空,但是一旦您解决了问题,就可能出现此问题。

public class TigerTrackingViewModel
{
    public TigerTrackingViewModel(){
       this.TigerTrail = new TigerTrail(); //Add this
    }

    public Guid YouthGuid { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public TigerTrail TigerTrail { get; set; }
}

应该对所有复杂属性执行此操作。

2)在显示项目集合时,您需要使用索引进行渲染,并为此将法线更改为以下项的法线:

@for (int i=0; i < Model.Count)
{
    @Html.HiddenFor(x => Model[i].TigerTrail)
    ...
    ...
}

对于您的情况,鉴于要显示大量数据,您应该删除所有视图代码以处理内部视图模型...,然后尝试是否获得发布的VM列表。 完成后,开始添加其他属性。

请记住:仅发布输入中的信息,因此...如果您需要发布而不是发布的内容,请至少检查是否将其隐藏。 另一件需要检查的是,你的索引( [i] )是不正确的。

在这里您可以看到有关绑定到列表的出色文章: http : //haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx (虽然有些陈旧,但基础知识仍然存在适用)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM