ASP.NET MVC – Model binding not occurring when posting list of complex types
ASP.NETMVCSome background — I am turning a gigantic Word form into an MVC application where each ‘page’ represents a small section of that form, and I had some trouble posting the model back to be saved. Lists of complex types — in my example, List — were coming back as null.
I have a class called Section.cs
:
public class Section
{
public string SectionName { get; set; }
public List<Field> SectionFields { get; set; }
}
Each section can have any number of Field objects — and each field has a value (this example has been simplified; the actual model has a lot more properties):
public class Field
{
public string Value { get; set; }
}
I created a view for my Section with a form. The SectionName property is just a string, so you can just call:
@html.EditorFor(model => model.SectionName)
MVC knows how to deal with a String, and will return the appropriate form input (you can, of course, create your own Editor Templates for simple types).
Field, on the other hand, is a complex type — and the best way to deal with complex types is to create a custom Editor Template (this is done by creating a partial view under /Views/EditorTemplates with the same name as your complex type):
@model Field
<label class="radio-inline">
@html.LabelFor(x => x.Value)
@html.TextBoxFor(x => x.Value)
</label>
In my Section view, I iterated over my list of Field objects and called @html.EditorFor() on each, knowing that I had an Editor Template set up.
@model Section
@html.EditorFor(model => model.SectionName)
@foreach(Field field in model.Fields)
{
@html.EditorFor(f => field)
}
I got a label and a textbox for each field in my list — but when I tried to post my form, the Fields object was always null.
It turns out (thank you http://stackoverflow.com/a/9143266/3257985) that the default MVC model binder makes it much simpler. Rather than iterating over the list of Field objects, I can just do:
@model Section
@html.EditorFor(model => model.SectionName)
@html.EditorFor(model => model.Fields)
If you look at the generated html for the Value property of each Field object, you will see why this works. Iterating over the list of fields produced this value for the input’s name attribute:
Section.field.Value
The model binder relies on the input’s name to work out what to do with the value — and this does not tell the model binder enough. It will find the Section object, but there is no such thing as ‘field’ (which comes from the use of ‘field’ here: @html.EditorFor(f => field)
This is what the name attribute looks like on the example that works:
Section.Fields[0].Value
The model binder can work out that you are targeting the Value property on the first object in the Fields list on the Section object. Model binding will work, and your complex type will be posted.
Ryan Couch
December 5, 2014 at 15:00
I am trying to understand this and I believe you need to rename all the code blocks that reference “model.fields” with model.SectionFields then the very last code block becomes: Section.SectionFields[0].Value
..or am I way off?
I am dealing with a similar issue and still struggling to make the model binding work. I am about to rip out the relevant code and make a really simple project just to prove it all out.
Ryan Couch
December 5, 2014 at 20:33
I finally solved my problem, and in the off chance it may help someone else…
{ get; set; }
on the list is super important 🙂my model class was defined like so:
changed to:
now the data is coming back from the controller and it makes total sense now why it didn’t before.
Martina
December 8, 2014 at 09:16
Glad you solved the problem! I have a feeling I had exactly this problem, so I’m glad you highlighted the issue in your comment.
Randall
September 29, 2015 at 03:03
This is an old post, but right now I’m in love with you!! Being trying to find why my model didn’t bind correctly and this was it.
Joe
February 15, 2017 at 09:51
I’ve had this exact problem, been working on it for a few hours now 😦 I’m not sure why changing from a public variable to a property is required…But your solution worked, thanks for sharing!
Diego
April 6, 2015 at 15:53
Hi!!! I’ve got something like that BUT my template has fields that user should fill, but those new values doesn’t go to my controller, after post. Do you have any idea?
thanks for your post, really helped me a lot
Martina
April 8, 2015 at 11:47
Hi! Would you be able to post your view & controller action? 🙂 Glad the post helped.
asdf
June 1, 2015 at 05:29
aww thats jsut super. until it comes time to creating and deleting items on the client and then MS will just fuck you up the arse once again
Momo Bachman
March 9, 2017 at 16:26
I have been trying to figure out why my view returned a null list for hours and it was something as simple as forgetting the
{get; set;}
in my model. THANK YOU SO MUCH, I was over-complicating things to the max.sylvia
April 18, 2017 at 16:01
Hi, I am pretty much in the same fix and have not been advised to use EditorFor template.So my Model is as below —
Hi, I am pretty much in the same fix and have not been advised to use EditorFor template.So my Model is as below
My View contains several partial views
and my controller function is as below –
When I try to post the controller is not able to get the Courses whose count =0