Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model Binding to a List MVC 4

Is there a pattern to bind an IList of items to the view. I seem to be having issues with the HttpPost. I know Phil Haack wrote a nice article but it is dated and he said they might have a fix with MVC 4.

like image 779
Karthik Avatar asked Mar 13 '13 02:03

Karthik


People also ask

How do you bind a model to view in MVC 5?

Right-click on the Create Action method and select Add View… It will display the Add View dialog. As you can see in the above screenshot, the default name is already mentioned. Now select Create from the Template dropdown and Employee from the Model class dropdown.


2 Answers

This is how I do it if I need a form displayed for each item, and inputs for various properties. Really depends on what I'm trying to do though.

ViewModel looks like this:

public class MyViewModel {    public List<Person> Persons{get;set;} } 

View(with BeginForm of course):

@model MyViewModel   @for( int i = 0; i < Model.Persons.Count(); ++i) {     @Html.HiddenFor(m => m.Persons[i].PersonId)     @Html.EditorFor(m => m.Persons[i].FirstName)      @Html.EditorFor(m => m.Persons[i].LastName)          } 

Action:

[HttpPost]public ViewResult(MyViewModel vm) { ... 

Note that on post back only properties which had inputs available will have values. I.e., if Person had a .SSN property, it would not be available in the post action because it wasn't a field in the form.

Note that the way MVC's model binding works, it will only look for consecutive ID's. So doing something like this where you conditionally hide an item will cause it to not bind any data after the 5th item, because once it encounters a gap in the IDs, it will stop binding. Even if there were 10 people, you would only get the first 4 on the postback:

@for( int i = 0; i < Model.Persons.Count(); ++i) {     if(i != 4)//conditionally hide 5th item,      { //but BUG occurs on postback, all items after 5th will not be bound to the the list       @Html.HiddenFor(m => m.Persons[i].PersonId)       @Html.EditorFor(m => m.Persons[i].FirstName)        @Html.EditorFor(m => m.Persons[i].LastName)                } } 
like image 188
AaronLS Avatar answered Sep 26 '22 10:09

AaronLS


A clean solution could be create a generic class to handle the list, so you don't need to create a different class each time you need it.

public class ListModel<T> {     public List<T> Items { get; set; }      public ListModel(List<T> list) {         Items = list;     } } 

and when you return the View you just need to simply do:

List<customClass> ListOfCustomClass = new List<customClass>(); //Do as needed... return View(new ListModel<customClass>(ListOfCustomClass)); 

then define the list in the model:

@model ListModel<customClass> 

and ready to go:

@foreach(var element in Model.Items) {   //do as needed... } 
like image 33
Javi Avatar answered Sep 22 '22 10:09

Javi