Running into some trouble with multiple forms on a single view.
Suppose I have the following viewmodel:
public class ChangeBankAccountViewModel
{
public IEnumerable<BankInfo> BankInfos { get; set; }
}
public class BankInfo
{
[Required]
public string BankAccount { get; set; }
public long Id { get; set; }
}
In my viewmodel, I want all BankInfos to be displayed underneath eachother, inside separate forms for each.
To achieve this, I'm using a partial view _EditBankInfo:
@model BankInfo
@using (Html.BeginForm())
{
@Html.HiddenFor(m => m.InvoiceStructureId)
@Html.TextBoxFor(m => m.IBANAccount)
<button type="submit">Update this stuff</button>
}
As well as my actual view BankInfo:
foreach(var info in Model.BankInfos)
{
Html.RenderPartial("_EditBankInfo", info);
}
Last, here are my 2 Action Methods:
[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();
}
All of this is working hunky dory: Validation works smooth, posted model gets recognized and validated correctly... However, when the page reloads is when the problem arises. Because I'm using the same form multiple times, my ModelState will be applied multiple times. So when performing an update on one form, the next page load all of them will have the posted values.
Is there any way to easily prevent this from happening?
I've tried doing it without the partial views, but that screws up the naming a bit (they're unique, but serverside modelbinding won't recognize them).
Thanks for any answers.
This is a bit tricky. Here's how it can be solved. Start by moving your
_EditBankInfo.cshtml
partial into an editor template~/Views/Shared/EditorTemplates/BankInfo.cshtml
that looks like this (notice that the name and location of the template is important. It should be placed inside~/Views/Shared/EditorTemplates
and named as the name of the typed used in yourIEnumerable<T>
collection property, which in your case isBankInfo.cshtml
):and then in your main view get rid of the
foreach
loop and replace it with a simple call to theEditorFor
helper:Now for each element of the
BankInfos
collection custom editor template will be rendered. And contrary to the partial, the editor template respects the navigational context and will generate the following markup:Now since every field has a specific name there will no longer be any conflicts when posting the form. Notice the hidden field named
model.prefix
that I explicitly placed inside each form. This will be used by a custom model binder for theBankInfo
type:which will be registered in your
Application_Start
:Alright, now we are good to go. Get rid of the
ModelState.Clear
in your controller action as you no longer need it: