2013-08-07 25 views
12

Chạy vào một số sự cố với nhiều biểu mẫu trên một chế độ xem.Nhiều biểu mẫu trong chế độ xem MVC: ModelState áp dụng cho tất cả các hình thức

Giả sử tôi có viewmodel sau:

public class ChangeBankAccountViewModel 
{ 
    public IEnumerable<BankInfo> BankInfos { get; set; } 
} 

public class BankInfo 
{ 
    [Required] 
    public string BankAccount { get; set; } 
    public long Id { get; set; } 
} 

Trong viewmodel của tôi, tôi muốn tất cả BankInfos sẽ được hiển thị bên dưới eachother, bên trong các hình thức riêng biệt cho mỗi.

Để đạt được điều này, tôi đang sử dụng một cái nhìn _EditBankInfo phần:

@model BankInfo 

@using (Html.BeginForm()) 
{ 
    @Html.HiddenFor(m => m.InvoiceStructureId) 
    @Html.TextBoxFor(m => m.IBANAccount) 

    <button type="submit">Update this stuff</button> 
} 

Cũng như xem BankInfo thực tế của tôi:

foreach(var info in Model.BankInfos) 
{ 
    Html.RenderPartial("_EditBankInfo", info); 
} 

ngoái, đây là 2 phương pháp hành động của tôi:

[HttpGet] 
public ActionResult BankInfo() 
{ 
    return View(new ChangeBankAccountViewModel{BankInfos = new [] {new BankInfo...}); 
} 
[HttpPost] 
public ActionResult BankInfo(BankInfo model) 
{ 
    if(ModelState.IsValid) 
     ModelState.Clear(); 
    return BankInfo(); 
} 

Tất cả điều này là làm việc hunky dory: Xác thực hoạt động trơn tru, đăng mô hình được công nhận và xác nhận chính xác ... Tuy nhiên, khi tải lại trang là khi sự cố phát sinh. Vì tôi đang sử dụng cùng một biểu mẫu nhiều lần, ModelState của tôi sẽ được áp dụng nhiều lần. Vì vậy, khi thực hiện cập nhật trên một biểu mẫu, trang tiếp theo tải tất cả chúng sẽ có các giá trị được đăng.

Có cách nào để dễ dàng ngăn điều này xảy ra không?

Tôi đã thử thực hiện nó mà không cần quan sát một phần, nhưng vít đặt tên lên một chút (chúng là duy nhất, nhưng modelide ràng buộc sẽ không nhận ra chúng).

Cảm ơn mọi câu trả lời.

+0

Bạn có thể hiển thị hành động của bộ điều khiển mà biểu mẫu được gửi không? Tôi đặc biệt quan tâm đến mô hình mà nó lấy làm tham số và mô hình nó chuyển đến khung nhìn. –

+0

@DarinDimitrov, Đã thêm chúng. Do biết đây là một ví dụ đơn giản, nhưng thiết lập cơ bản nên ở đó. Ngoài ra, tôi có thể sẽ sử dụng một số loại kịch bản PRG ở đây. – Kippie

Trả lời

10

Đây là một chút khó khăn. Đây là cách nó có thể được giải quyết. Bắt đầu bằng cách di chuyển _EditBankInfo.cshtml một phần của bạn thành mẫu biên tập ~/Views/Shared/EditorTemplates/BankInfo.cshtml trông giống như thế này (chú ý rằng tên và vị trí của mẫu là quan trọng. Tên này phải được đặt bên trong ~/Views/Shared/EditorTemplates và được đặt tên là tên được nhập sử dụng trong thuộc tính thu thập IEnumerable<T> của bạn trong trường hợp của bạn là BankInfo.cshtml):

@model BankInfo 

<div> 
    @using (Html.BeginForm()) 
    { 
     <input type="hidden" name="model.prefix" value="@ViewData.TemplateInfo.HtmlFieldPrefix" /> 
     @Html.HiddenFor(m => m.Id) 
     @Html.TextBoxFor(m => m.BankAccount) 

     <button type="submit">Update this stuff</button> 
    } 
</div> 

và sau đó trong giao diện chính của bạn thoát khỏi những foreach vòng lặp và thay thế bằng một cuộc gọi đơn giản để các EditorFor helper:

@model ChangeBankAccountViewModel 

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

Bây giờ cho mỗi phần tử của mẫu biên tập tùy chỉnh bộ sưu tập BankInfos sẽ được hiển thị. Và trái với một phần, mẫu biên tập tôn trọng bối cảnh hàng hải và sẽ tạo ra các đánh dấu sau:

<div> 
    <form action="/" method="post">  
     <input type="hidden" name="model.prefix" value="BankInfos[0]" /> 
     <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="BankInfos_0__Id" name="BankInfos[0].Id" type="hidden" value="1" /> 
     <input data-val="true" data-val-required="The BankAccount field is required." id="BankInfos_0__BankAccount" name="BankInfos[0].BankAccount" type="text" value="account 1" />  
     <button type="submit">Update this stuff</button> 
    </form> 
</div> 

<div> 
    <form action="/" method="post">  
     <input type="hidden" name="model.prefix" value="BankInfos[1]" /> 
     <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="BankInfos_1__Id" name="BankInfos[1].Id" type="hidden" value="2" /> 
     <input data-val="true" data-val-required="The BankAccount field is required." id="BankInfos_1__BankAccount" name="BankInfos[1].BankAccount" type="text" value="account 2" />  
     <button type="submit">Update this stuff</button> 
    </form> 
</div> 

... 

Bây giờ vì mỗi lĩnh vực có một cái tên cụ thể sẽ không còn có bất kỳ mâu thuẫn khi đăng biểu mẫu. Lưu ý trường ẩn có tên là model.prefix mà tôi đặt rõ ràng bên trong mỗi biểu mẫu.Điều này sẽ được sử dụng bởi một chất kết dính mô hình tùy chỉnh cho các loại BankInfo:

public class BankInfoModelBinder: DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     bindingContext.ModelName = controllerContext.HttpContext.Request.Form["model.prefix"]; 
     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

sẽ được đăng ký tại Application_Start của bạn:

ModelBinders.Binders.Add(typeof(BankInfo), new BankInfoModelBinder()); 

Được rồi, bây giờ chúng tôi đang tốt để đi. Loại bỏ các ModelState.Clear trong hành động điều khiển của bạn khi bạn không còn cần đến nó:

[HttpGet] 
public ActionResult BankInfo() 
{ 
    var model = new ChangeBankAccountViewModel 
    { 
     // This is probably populated from some data store 
     BankInfos = new [] { new BankInfo... }, 
    } 
    return View(model); 
} 

[HttpPost] 
public ActionResult BankInfo(BankInfo model) 
{ 
    if(ModelState.IsValid) 
    { 
     // TODO: the model is valid => update its value into your data store 
     // DO NOT CALL ModelState.Clear anymore. 
    } 

    return BankInfo(); 
} 
+0

Cảm ơn rất nhiều, Darin. Ví dụ tuyệt vời đã dạy tôi một vài thủ thuật (ai biết rằng EditorFor cho phép sưu tập Enumerable ??). Chỉ cần có một vấn đề nhỏ trong việc tìm ra tiền tố trong trường hợp tôi cần phải tự thêm một lỗi mô hình để modelstate của tôi, nhưng tôi chắc chắn tôi sẽ tìm ra một trong đó. – Kippie

Các vấn đề liên quan