编辑和保存复杂对象

Bra*_*tie 7 asp.net-mvc-templates razor asp.net-mvc-4

假设您有以下对象:

public class Address
{
  public String Line1 { get; set; }
  public String Line2 { get; set; }
  public String City { get; set; }
  public String State { get; set; }
  public String ZipCode { get; set; }

  public Address()
  {
  }
}

public class Contact
{
  public String FirstName { get; set; }
  public String LastName { get; set; }
  public String Telephone { get; set; }

  public Address BillingAddress { get; set; }
  public List<Address> ShippingAddresses { get; set; }

  public Contact()
  {
    // Assume anything that _could_ be null wouldn't be. I'm excluding
    // most "typical" error checking just to keep the examples simple
    this.BillingAddress = new Address();
    this.ShippingAddresses = new List<Address>();
  }
}
Run Code Online (Sandbox Code Playgroud)

假设性与装饰[Required],[Display]和其他属性.

然后是我的控制器(为了演示而简化):

public ActionResult Edit(String id)
{
  Contact contact = ContactManager.FindByID(id);
  return View(model: contact);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Contact contact)
{
  if (ModelState.IsValid) //always fails
  {
    ContactManager.Save(contact);
    return RedirectToAction("Saved");
  }
  return View(model: contact);
}
Run Code Online (Sandbox Code Playgroud)

我一直看到在MVC中编辑像这样的对象的演示,但他们不断地将对象的集合分解为他们自己的形式(例如编辑联系人,然后编辑联系人的特定地址).另一方面,我试图在同一页面内编辑所有这些信息,但没有成功.例如:

@model Contact

// simplified for brevity
@using (Html.BeginForm())
{
  @Html.LabelFor(x => x.FirstName): @Html.EditorFor(x => x.FirstName)
  @Html.LabelFor(x => x.LastName): @Html.EditorFor(x => x.LastName)
  @Html.LabelFor(x => x.Telephone): @Html.EditorFor(x => x.Telephone)

  <div>
    @Html.LabelFor(x => x.BillingAddress.Line1): @Html.EditorFor(x => x.BillingAddress.Line1)
    @Html.LabelFor(x => x.BillingAddress.Line2): @Html.EditorFor(x => x.BillingAddress.Line2)
    @Html.LabelFor(x => x.BillingAddress.City): @Html.EditorFor(x => x.BillingAddress.City)
    @Html.LabelFor(x => x.BillingAddress.State): @Html.EditorFor(x => x.BillingAddress.State)
    @Html.LabelFor(x => x.BillingAddress.ZipCode): @Html.EditorFor(x => x.BillingAddress.ZipCode)
  </div>

  <div>
  @foreach (Address addr in Model.ShippingAddresses)
  {
    <div>
      @Html.LabelFor(x => addr.Line1): @Html.EditorFor(x => addr.Line1)
      @Html.LabelFor(x => addr.Line2): @Html.EditorFor(x => addr.Line2)
      @Html.LabelFor(x => addr.City): @Html.EditorFor(x => addr.City)
      @Html.LabelFor(x => addr.State): @Html.EditorFor(x => addr.State)
      @Html.LabelFor(x => addr.ZipCode): @Html.EditorFor(x => addr.ZipCode)
    </div>
  }
  </div>
}
Run Code Online (Sandbox Code Playgroud)

我一直在努力的问题是,ModelState.IsValid当我去保存信息时,永远不会通过.这样做有诀窍,还是仅仅在MVC之外?我想拿一个像这样的对象Contact并将所有信息转储到一个页面上进行编辑,并让它重新保存成功,但我似乎无法让它工作.(我的下一步是绑定ajax,以便您可以在该页面上动态添加/删除"ShipingAddresses",但我需要首先保存 - KISS)

问题:

  • ModelState.IsValid 几乎总是假的
  • 集合项的表单元素通常具有相同的名称,因此在此演示Line1中,ShippingAddresses集合中的每个元素都转储到页面,name="addr_Line1"而不是ShippingAddresses[0]_Line1像我期望的那样.

Dar*_*rov 10

我猜ModelState.IsValid是错误的,因为你没有正确填充送货地址集合.发生这种情况是因为您没有为输入字段使用专有名称.请查看以下文章,以更好地了解模型绑定器期望命名输入字段的格式,以便能够重新构建值.

您的代码没有生成正确的输入名称的原因是因为您使用了此foreach循环,其中视图丢失了导航上下文的跟踪.

所以尝试这样:

@for (var i = 0; i < Model.ShippingAddresses.Count; i++)
{
    <div>
        @Html.LabelFor(x => x.ShippingAddresses[i].Line1): 
        @Html.EditorFor(x => x.ShippingAddresses[i].Line1)

        @Html.LabelFor(x => x.ShippingAddresses[i].Line2): 
        @Html.EditorFor(x => x.ShippingAddresses[i].Line2)

        @Html.LabelFor(x => x.ShippingAddresses[i].City): 
        @Html.EditorFor(x => x.ShippingAddresses[i].City)

        @Html.LabelFor(x => x.ShippingAddresses[i].State): 
        @Html.EditorFor(x => x.ShippingAddresses[i].State)

        @Html.LabelFor(x => x.ShippingAddresses[i].ZipCode): 
        @Html.EditorFor(x => x.ShippingAddresses[i].ZipCode)
    </div>    
}
Run Code Online (Sandbox Code Playgroud)

备注:请注意,我还使用EditorFor调用替换了您的重复标签,以有效地为这些字段生成输入字段.

甚至更好地使用这样的编辑器模板:

@model Contact

@using (Html.BeginForm())
{
    @Html.LabelFor(x => x.FirstName): 
    @Html.EditorFor(x => x.FirstName)

    @Html.LabelFor(x => x.LastName): 
    @Html.EditorFor(x => x.LastName)

    @Html.LabelFor(x => x.Telephone): 
    @Html.EditorFor(x => x.Telephone)

    @Html.EditorFor(x => x.BillingAddress)

    @Html.EditorFor(x => x.ShippingAddresses)
}
Run Code Online (Sandbox Code Playgroud)

然后定义一个自定义编辑器模板,该模板将自动为ShippingAddresses集合的每个元素呈现(~/Views/Shared/EditorTemplates/Address.cshtml):

@model Address
<div>
    @Html.LabelFor(x => x.Line1): 
    @Html.EditorFor(x => x.Line1)

    @Html.LabelFor(x => x.Line2): 
    @Html.EditorFor(x => x.Line2)

    @Html.LabelFor(x => x.City): 
    @Html.EditorFor(x => x.City)

    @Html.LabelFor(x => x.State): 
    @Html.EditorFor(x => x.State)

    @Html.LabelFor(x => x.ZipCode): 
    @Html.EditorFor(x => x.ZipCode)
</div>
Run Code Online (Sandbox Code Playgroud)

现在您不再担心输入错误的输入名称.不仅如此,您还要重复使用地址编辑器模板来计算帐单地址和送货地址.它使你的观点DRYier.