Reputation: 941
I would like to have multiple forms on a single page showing each one on a fancy tab. I thought I would create a container model which would hold the models the work would actually happen on. Then I would create handlers for each form(/tab) in the controller accepting the specific model as its parameter I want to work with.
Consider the following models:
public class FormCollection
{
public FormsContainer()
{
Form1 = new Form1();
Form2 = new Form2();
}
public Form1 Form1 { get; set; }
public Form2 Form2 { get; set; }
}
public class Form1
{
public string PropNameCollision { get; set; }
public DateTime? Form1Date { get; set; }
}
public class Form2
{
public string PropNameCollision { get; set; }
public DateTime? Form2Date { get; set; }
}
In the FormController
controller:
public ActionResult Form1Handler(Form1 model)
{
return Content("Doing Form1");
}
public ActionResult Form2Handler(Form2 model)
{
return Content("Doing Form2");
}
And a view:
@model MvcApp.Models.FormCollection
<section id="tab1">
@using (Html.BeingForm("Form1Handler", "Form"))
{
@Html.TextboxFor(m => m.Form1.PropNameCollision)
@Html.TextboxFor(m => m.Form1.Form1Date)
<input type="submit"/>
}
</section>
<section id="tab2">
@using (Html.BeingForm("Form2Handler", "Form"))
{
@Html.TextboxFor(m => m.Form2.PropNameCollision)
@Html.TextboxFor(m => m.Form2.Form2Date)
<input type="submit"/>
}
</section>
When I submit either form, the default model binder can't match up the model and what arrived in the context because e.g. to bind Form1
's PropNameCollision
it would expect a value for PropNameCollision
but instead Form1.PropNameCollision
arrives, because that's what the raw HTML markup generated by the helper:
<input type="text" id="Form1_PropNameCollision" name="Form1.PropNameCollision" ... />
The question:
Is there a smart way to create a binder that looks for a specific type in the context and binds+returns only that? I've doodled a bit with overriding the default binder's BindModel
, managed to bind primitives with Reflection, but the path did not seem favourable (accounting for complex types, nullables, etc.).
Edit: I would like to avoid accepting FormCollection
models, because I would like to keep my hands tied, meaning I don't want to accidentally work with data I'm not supposed to work with. Say someone else needs to work with the code, or I'm coming back to it 6 months from now and I forgot everything about needing to separate down the sub-class.
Upvotes: 1
Views: 1078
Reputation: 7140
If I understand your situation correctly, then you might want to try a custom model binder as described in the accepted answer to this question. To summarise:
@Html.EditorFor
to display the form sections related to the various class instances.Depending on your controller logic, you may still have to receive them as base class instances and either do some logic to work out what to cast them to (not very pretty, but it works) or give the base class the relevant methods so that the derived classes can provide their own implementations and take advantage of a little polymorphism (a more elegant approach, but sometimes trickier).
Upvotes: 0
Reputation: 21881
Put your forms in partial views then bind them to your properties like so
@Html.Partial("PartialViewForm1", model.Form1)
@Html.Partial("PartialViewForm2", model.Form2)
Then your main view can be strongly typed to FormsContainer and your partial view can be strngly typed to Form1 and Form2.
Although in your case i would have only a single class called form, as the properties are identical on both classes and simply have 2 properties that are of this type.
Upvotes: 1