Ответ 1
Это немного сложно. Вот как это можно решить. Начните с перемещения части _EditBankInfo.cshtml
в шаблон редактора ~/Views/Shared/EditorTemplates/BankInfo.cshtml
, который выглядит так (обратите внимание, что имя и расположение шаблона важно. Оно должно быть помещено внутри ~/Views/Shared/EditorTemplates
и названо именем имени, используемого в ваше свойство коллекции IEnumerable<T>
, которое в вашем случае 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>
а затем в главном представлении избавиться от цикла foreach
и заменить его простым вызовом помощнику EditorFor
:
@model ChangeBankAccountViewModel
@Html.EditorFor(x => x.BankInfos)
Теперь для каждого элемента шаблона редактора шаблона BankInfos
будет отображаться шаблон. И в отличие от частичного, шаблон редактора учитывает навигационный контекст и генерирует следующую разметку:
<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>
...
Теперь, поскольку у каждого поля есть определенное имя, больше не будет конфликтов при отправке формы. Обратите внимание на скрытое поле с именем model.prefix
, которое я явно помещаю внутри каждой формы. Это будет использоваться специальным связующим устройством для типа 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);
}
}
который будет зарегистрирован в вашем Application_Start
:
ModelBinders.Binders.Add(typeof(BankInfo), new BankInfoModelBinder());
Хорошо, теперь нам хорошо идти. Избавьтесь от ModelState.Clear
в действии вашего контроллера, поскольку он вам больше не нужен:
[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();
}