7

次のオブジェクトがあるとします。

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

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

私は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>
}

私が遭遇し続ける問題はModelState.IsValid、情報を保存しに行くときに決して通過しないということです。これを行うためのトリックはありますか、それともMVCの領域のすぐ外にありますか?のようなオブジェクトを取得Contactして、編集のためにすべての情報を1ページにダンプし、正常に再保存してもらいたいのですが、動作させることができないようです。(私の次のステップは、ajaxを結び付けて、そのページの「ShipingAddresses」を動的に追加/削除できるようにすることですが、最初に保存を機能させる必要があります--KISS)

問題:

  • ModelState.IsValidほとんどの場合false
  • コレクションアイテムのフォーム要素は同じ名前であることが多いため、このデモLine1では、コレクション内のすべてが、私が期待するようなものではなくShippingAddresses、ページにダンプされます。name="addr_Line1"ShippingAddresses[0]_Line1
4

1 に答える 1

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

注: 重複するラベルを 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)
}

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>

これで、間違った入力名を取得することを心配する必要がなくなりました。これだけでなく、請求先住所と配送先住所のコレクションの両方に住所エディター テンプレートを再利用しています。それはあなたの見解をよりドライにします。

于 2012-01-27T20:12:20.763 に答える