MVC3仅保留已发布的表单值?

时间:2011-05-16 16:23:12

标签: asp.net-mvc-3

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

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);
}

5 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

我通常在隐藏的输入中回复这样的信息。我个人大量使用它来传递所需的数据,以便在按下编辑之前将用户完全返回到哪里。

在你的情况下,它就像

一样简单
@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;
}

如果你传递Arrays,你可以这样做

查看

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

动作

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

答案 2 :(得分:0)

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

一般来说,你有两个选择:

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

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

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

答案 3 :(得分:0)

我知道这个帖子有点旧,但我想我会复活它以获得对我的解决方案的反馈。

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

我不喜欢为每个视图创建模型的想法。我知道这可能是正确的方式......但我正在寻找可以相当快速地实现的可重用模式。 请参阅“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复制/粘贴到每个模型对象。

答案 4 :(得分:0)

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