【问题标题】:MVC3 Only posted form values retained?MVC3 仅保留已发布的表单值?
【发布时间】:2013-12-11 16:38:58
【问题描述】:

我在 MVC3 Web 应用程序中使用强类型视图。我注意到,当提交表单时,传递给控制器​​的 ViewModel 仅具有与表单元素关联的属性的值。例如,下面的示例显示了一个简单的确认视图,其中包含一个复选框和一个用户必须在继续之前确认的电话号码。当表单提交给控制器操作时,UserConfirmed 属性包含一个值,但 PhoneNumber 属性为空。

有什么方法可以让 ViewModel 保留其所有值,还是我必须重新填充没有关联表单元素的 ViewModel 属性?

观点

@model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
@using (Html.BeginForm()) {
@Html.ValidationSummary(false)

@Html.CheckBoxFor(model => model.UserConfirmed) 
<span>Please confirm before proceeding</span>
<div>
    Phone Number: @Model.PhoneNumber
</div>
<input type="submit" value="Confirm"/>

控制器

[HttpPost]
public ActionResult ScheduleConfirmation(ScheduleConfirmationViewModel model)
{
if (model.UserConfirmed)
{
    // add ViewModel data to repository
}
else
{
    ModelState.AddModelError("ERROR", WebResources.strERROR_ConfirmSchedule);
}

return View(model);
}

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    由于您将电话号码作为输出写入页面,因此不会自动回发(您已经发现了该部分)您可以做的是使用电话号码填充隐藏或只读字段,以便它将发回您的控制器。第二种选择是重新调用您的数据源并重新填充您的对象,然后再将其保存回您的数据源。

    【讨论】:

      【解决方案2】:

      我通常在隐藏的输入中回传这样的信息。我个人大量使用它来传递所需的数据,以便在按下编辑之前准确地返回用户所在的位置。

      在你的情况下,它就像

      一样简单
      @model WebMeterReplacement.ViewModels.Appointment.ScheduleConfirmationViewModel
      @using (Html.BeginForm()) {
      @Html.ValidationSummary(false)
      
      @Html.CheckBoxFor(model => model.UserConfirmed) 
      <span>Please confirm before proceeding</span>
      <div>
          @Html.HiddenFor(m => m.PhoneNumber)
          Phone Number: @Model.PhoneNumber
      </div>
      <input type="submit" value="Confirm"/>
      

      供将来参考:

      如果您传递复杂的对象,则每个属性需要一个隐藏字段(Hiddenfor 不会迭代)

      查看

      WRONG
      @Html.HiddenFor(m => m.PagingData)
      
      RIGHT
      @Html.HiddenFor(m => m.PagingData.Count)
      @Html.HiddenFor(m => m.PagingData.Skip)
      @Html.HiddenFor(m => m.PagingData.PageSize)
      

      动作

      public HomeController(AViewModel Model)
      {
         PagingData PagingData = Model.PagingData;
         Skip = PagingData.Skip;
      }
      

      如果你传递数组,你可以这样做

      查看

      @if (Model.HiddenFields != null)
      {   
      foreach (string HiddenField in Model.HiddenFields)
      {
          @Html.Hidden("HiddenFields", HiddenField)    
      }
      }
      

      动作

      public HomeController(AViewModel Model)
      {
          String[] HiddenFields = Model.HiddenFields;
      }
      

      【讨论】:

        【解决方案3】:

        好吧,表单只会发布您创建的元素。正如您所发现的,仅将电话号码写到页面上是不够的。模型绑定器只能绑定已发布数据中存在的那些属性。

        通常你有几个选择:

        1) 您可以为模型中的所有属性创建 Input 元素,为要编辑的属性使用可见元素(如文本框),以及应回发但没有 UI 的隐藏元素

        2)发回模型的部分表示(就像您现在所做的那样),从它的数据源中读回实体(我假设您正在使用某种数据源,可能是 EF),然后更改该实体的属性与您的表单中的属性。

        这两种情况都很常见,但这实际上取决于模型的复杂性。

        【讨论】:

          【解决方案4】:

          我知道这个帖子有点老了,但我想我会重新发布它以获取我对此问题的解决方案的反馈。

          我处于类似的情况,我的对象被传递给视图,而视图可能只显示该对象的一部分以进行编辑。显然,当控制器从默认模型绑定器接收到模型时,未回发的值变为空。保存这意味着数据库值变为空,只是因为它没有从视图中显示/返回。

          我不喜欢为每个视图创建模型的想法。我知道这可能是正确的方式...但我一直在寻找一种可以相当快速地实现的可重用模式。 请参阅“MergeWith”方法...因为这将用于从数据库中获取对象的副本并将其与从视图返回的对象合并(回发)

              namespace SIP.Models
          {
              [Table("agents")]
              public class Agent
              {
                  [Key]
                  public int id { get; set; }
          
                  [Searchable]
                  [DisplayName("Name")]
                  [Column("name")]
                  [Required]
                  [StringLength(50, MinimumLength = 4)]
                  public string AgentName { get; set; }
          
                  [Searchable]
                  [DisplayName("Address")]
                  [Column("address")]
                  [DataType(DataType.MultilineText)]
                  public string Address { get; set; }
          
          
                  [DisplayName("Region")]
                  [Searchable]
                  [Column("region")]
                  [StringLength(50, MinimumLength = 3)]
                  public string Region { get; set; }
          
          
                  [DisplayName("Phone")]
                  [Column("phone")]
                  [StringLength(50, MinimumLength = 4)]
                  public string Phone { get; set; }
          
                  [DisplayName("Fax")]
                  [Column("fax")]
                  [StringLength(50, MinimumLength = 4)]
                  public string Fax { get; set; }
          
          
                  [DisplayName("Email")]
                  [RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed")]
                  [Column("email")]
                  [StringLength(50, MinimumLength = 4)]
                  public string Email { get; set; }
          
          
                  [DisplayName("Notes")]
                  [Column("notes")]
                  [DataType(DataType.MultilineText)]
                  public string Notes{ get; set; }
          
                  [DisplayName("Active")]
                  [Column("active")]
                  public bool Active { get; set; }
          
          
                  public override string ToString()
                  {
                      return AgentName;
                  }
          
                  public bool MergeWith(Agent a, string[] fields)
                  {
                      try
                      {
                          foreach (PropertyInfo pi in this.GetType().GetProperties())
                          {
                              foreach (string f in fields)
                              {
                                  if (pi.Name == f && pi.Name.ToLower() != "id")
                                  {
                                      var newVal = a.GetType().GetProperty(f).GetValue(a,null);
                                      pi.SetValue(this, newVal, null);
                                  }
                              }
                          }
                      }
                      catch (Exception ex)
                      {
                          return false;
                          //todo: Log output to file...
                      }
          
                      return true;
                  }
              }
          }
          

          要在控制器中使用它......你会有类似的东西......

           [HttpPost]
              public ActionResult Edit(Agent agent)
              {
          
          
                  if (ModelState.IsValid)
                  {
                      Agent ag = db.Agents.Where(a => a.id == agent.id).ToList<Agent>().First<Agent>();
                      ag.MergeWith(agent, Request.Params.AllKeys);
          
                      db.Entry(ag).State = EntityState.Modified;
                      db.SaveChanges();
                      return RedirectToAction("Index");
                  }
                  return View(agent);
              }
          

          这样,在回发期间,它从数据库中获取对象,并使用视图中的对象更新它......但只更新回发的值。所以如果你有一个像“地址”或没有出现在视图中的东西..在更新过程中它没有被触及。

          到目前为止,我已经对此进行了测试,并且我为自己的目的工作,但我欢迎任何反馈,因为我很想看看其他人如何克服这种情况。这是第一个版本,我确信它可以通过扩展方法或其他方式更好地实现。但现在 MergeWith 可以复制/粘贴到每个模型对象。

          【讨论】:

            【解决方案5】:

            是的,只需在表单中为那些您不使用并希望返回服务器控制的值放置隐藏字段。 谢谢

            【讨论】:

              猜你喜欢
              • 2012-03-14
              • 2020-10-17
              • 1970-01-01
              • 2011-11-01
              • 2022-01-06
              • 1970-01-01
              • 1970-01-01
              • 2014-11-29
              相关资源
              最近更新 更多