【问题标题】:Passing Model data from View to Controller将模型数据从视图传递到控制器
【发布时间】:2014-12-18 22:42:15
【问题描述】:

我正在尝试通过 HttpPost 将模型数据从视图(以及视图中的 PartialView)传回控制器。 (改编自Pass SelectedValue of DropDownList in Html.BeginForm() in ASP.NEt MVC 3)

为什么?我想显示一个资产列表,每个资产都有一个 DropDownList 和选项数量。提交表单后从 DropDownList 中读取所选项目。

我的 2 个(简化)模型:

public class Booking
{
    public int BookingID { get; set; }
    public int StoreID { get; set; }
    ...
    public IEnumerable<AssetShort> Assets { get; set; }
}

和

public class AssetShort
{
    public int AssetID { get; set; }
    ....
    public int SelectedAction { get; set; }
    public IEnumerable<SelectListItem> ActionList { get; set; }
}

在我的 Booking Controller > Create 中构建列表:

public ActionResult Booking(int id)
    {
     // get myBag which contains a List<Asset>
     //  booking corresponds to 'id'

        var myAssets = new List<AssetShort>();
        foreach (var a in myBag.Assets)
        {
            var b = new AssetShort();
            b.AssetID = a.ID;
            b.SelectedAction = 0;
            b.ActionList = new[]
                { 
            new SelectListItem { Selected = true, Value = "0", Text = "Select..."},
            new SelectListItem { Selected = false, Value = "1", Text = "Add"},
            new SelectListItem { Selected = false, Value = "2", Text = "Remove"},
            new SelectListItem { Selected = false, Value = "3", Text = "Relocate"},
            new SelectListItem { Selected = false, Value = "4", Text = "Upgrade"},
            new SelectListItem { Selected = false, Value = "5", Text = "Downgrade"}
                };
            myAssets.Add(b);
        };

        var model = new BookingRequirementsViewModel
            {
                BookingID = booking.ID,
                StoreID = booking.StoreID,
                Assets = myAssets.ToList(),
            };
        return View(model);

我的观点:

@model uatlab.ViewModels.BookingRequirementsViewModel
@{
    ViewBag.Title = "Booking step 2";
}
<h4>Your booking ref. @Model.BookingID</h4>
@using (Html.BeginForm("Booking2", "Booking", FormMethod.Post))
{
<fieldset>
  @Html.AntiForgeryToken()
  @Html.HiddenFor(model => model.StoreID)

  @Html.Partial("_Assets", Model.StoreAssets)

  <input type="submit" value="Cancel" class="btn btn-default" />
  <input type="submit" value="Next" class="btn btn-default" />
</fieldset>
}

部分视图包括

@foreach (var item in Model)
{
    <tr>
        <td>@item.Name</td>
        <td>@item.Number</td>
        <td>@Html.DropDownListFor(modelItem=>item.SelectedAction, item.ActionList)</td>
    </tr>
}

因此,所有这些在浏览器中都可以正常工作,我可以为列出的每个资产选择下拉列表,但是当我提交时,唯一回发的值是 StoreID,因为它在“HiddenFor”中。

booking2 控制器有一个参数的模型:

public ActionResult Booking2(BookingRequirementsViewModel model)
{
    //loop through model.Assets and display SelectedActions
}

让我澄清一下问题所在 - 在 Booking2 控制器中,在调试模式下查看时模型为空,并且我收到错误“对象引用未设置为对象的实例”。

请教如何将模型从视图传回控制器?

问候 克雷格

【问题讨论】:

  • 您不能使用分部在集合中生成控件。如果您检查 html,您将看到您有重复的 name 属性(以及重复的 id 属性,这是无效的 html)。您需要在主视图中使用for 循环,或者为Assets 使用自定义EditorTemplate

标签: asp.net-mvc http-post


【解决方案1】:

您需要为AssetShort 创建一个EditorTemplate。我还建议将ActionList 移动到BookingRequirementsViewModel,这样您就不会为每个AssetShort 重新生成一个新的SelectList

您发布的模型没有意义。您的控制器有var model = new BookingRequirementsViewModel { ..., Assets = myAssets.ToList() };,但在视图中您指的是@Html.Partial("_Assets", Model.StoreAssets)?这是2个不同的属性。我会假设StoreAssets 是IEnumerable&lt;AssetShort&gt;

/Views/Shared/EditorTemplates/AssetShort.cshtml

@model AssetShort
<tr>
  <td>@Html.DispayFor(m => m.Name)</td>
  ....
  <td>
    @Html.DropDownListFor(m => m.SelectedAction, (IEnumerable<SelectListItem>)ViewData["actionList"], "--Please select--")
    @Html.ValidationMessageFor(m => m.SelectedAction)
  </td>
</tr>

在主视图中

@model uatlab.ViewModels.BookingRequirementsViewModel
....
@using (Html.BeginForm()) // Not sure why you post to a method with a different name
{
  ....
  @Html.HiddenFor(m => m.StoreID)
  @Html.EditorFor(m => m.StoreAssets, new { actionList = Model.ActionList })
  ....
}

在控制器中

public ActionResult Booking(int id)
{
  ....
  var model = new BookingRequirementsViewModel
  {
    BookingID = booking.ID,
    StoreID = booking.StoreID,
    Assets = myBag.Assets.Select(a => new AssetShort()
    {
      AssetID = a.ID,
      SelectedAction = a.SelectedAction, // assign this if you want a selected option, otherwise the "--Please select--" option will be selected
      ....
    })
  };
  ConfigureViewModel(model); // Assign select list
  return View(model);
}

还有一个单独的方法来生成SelectList,因为如果您返回视图,则需要在 GET 方法中调用它,并在 POST 方法中再次调用它。注意使用DropDownListFor()的重载生成选项标签(空值)如上,设置Selected属性没有意义(SelectedAction的值决定选择什么,不是这个)

private ConfigureViewModel(BookingRequirementsViewModel model)
{
  model.ActionList = new[]
  {
    new SelectListItem { Value = "1", Text = "Add"},
    ....
    new SelectListItem { Value = "5", Text = "Downgrade"}
  };
}

和 POST

public ActionResult Booking(BookingRequirementsViewModel model)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(model); // Re-assign select list
    return View(model);
  }
  // save and redirect
}

我建议还使用 [Required] 属性使 SelectedAction 可以为空,以便您获得客户端和服务器端验证

public class AssetShort
{
  public int AssetID { get; set; }
  ....
  [Required]
  public int? SelectedAction { get; set; }
}

【讨论】:

  • Stephen 请提一个问题... Booking 控制器中的行,在 BookingRequirementsModel 模型中填充 AssetShort 列表:SelectedAction = a.SelectedAction,这看起来我也需要 Assets 模型中的 SelectedAction,我目前没有。那正确吗?问候
  • 我只是假设你拥有它。如果您实际上没有将所选选项的值保存到数据库中,那么我想您不需要它,但在这种情况下,AssetShort 中它的实际用途是什么?
  • 斯蒂芬 - 一件事没有按预期工作。 POST 中返回的模型不包括 GET 中的所有模型值,除了 HiddenFor 中的 StoreID。如果我在 HiddenFor 中包含 StoreAssets,则 ModelState 无效。我必须在主视图中做些什么吗?问候
  • AssetShort 中 SelectedAction 的重新目的是知道在该点对资产采取什么行动。例如添加一个。我可以将它记录到数据库中,但目前没有这种需要。更多的问题是在发布时访问模型数据,因为我无法确定为哪个资产选择了哪个操作,除非我再次询问数据库以获取似乎浪费的列表。
  • N为StoreAssets返回的唯一属性是SelectedAction的值(下拉菜单)如果您还想回复说AssetID,那么您将需要@Html.TextBoxFor()或@ 987654348@。但是正如我所提到的,您没有发布所有模型,并且您所做的事情令人困惑,并且似乎与您的控制器或视图代码不匹配,很难说。不确定您所说的 include StoreAssets in a HiddenFor? (您不能使用HiddenFor() 绑定到复杂对象,只能绑定到值类型。
猜你喜欢
  • 2010-11-25
  • 1970-01-01
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多