MVC How to pass a list of objects with List Items POST action method
asp.net-mvc-3, razor
Solution
You're finding this difficult because you're not utilising the full power of the MVC framework, so allow me to provide a working example.
First up, let's create a view model to encapsulate your view's data requirements:
public class SubjectGradesViewModel
{
public SubjectGradesViewModel()
{
Subjects = new List<Subject>();
}
public List<Subject> Subjects { get; set; }
}
Next, create a class to represent your subject model:
public class Subject
{
public int Id { get; set; }
public string Name { get; set; }
public List<Student> StudentEntries { get; set; }
}
Finally, a class to represent a student:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Grade { get; set; }
}
At this point, you have all the classes you need to represent your data. Now let's create two controller actions, including some sample data so you can see how this works:
public ActionResult Index()
{
var model = new SubjectGradesViewModel();
// This sample data would normally be fetched
// from your database
var compsci = new Subject
{
Id = 1,
Name = "Computer Science",
StudentEntries = new List<Student>()
{
new Student { Id = 1, Name = "CompSci 1" },
new Student { Id = 2, Name = "CompSci 2" },
}
};
var maths = new Subject
{
Id = 2,
Name = "Mathematics",
StudentEntries = new List<Student>()
{
new Student { Id = 3, Name = "Maths 1" },
new Student { Id = 4, Name = "Maths 2" },
}
};
model.Subjects.Add(compsci);
model.Subjects.Add(maths);
return View(model);
}
[HttpPost]
public ActionResult Index(SubjectGradesViewModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Success");
}
// There were validation errors
// so redisplay the form
return View(model);
}
Now it's time to construct the views, and this part is particularly important when it comes to sending data back to a controller. First up is the `Index` view:
@model SubjectGradesViewModel
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.EditorFor(m => m.Subjects) <br />
<input type="submit" />
}
You'll notice I'm simply using `Html.EditorFor`, whilst passing `Subjects` as the parameter. The reason I'm doing this is because we're going to create an `EditorTemplate` to represent a `Subject`. I'll explain more later on. For now, just know that `EditorTemplates` and `DisplayTemplates` are special folder names in MVC, so their names, and locations, are important.
We're actually going to create two templates: one for `Subject` and one for `Student`. To do that, follow these steps:
- Create an `EditorTemplates` folder inside your view's current folder (e.g. if your view is `Home\Index.cshtml`, create the folder `Home\EditorTemplates`).
- Create a strongly-typed view in that directory with the name that matches your model (i.e. in this case you would make two views, which would be called `Subject.cshtml` and `Student.cshtml`, respectively (again, the naming is important)).
`Subject.cshtml` should look like this:
@model Subject
<b>@Model.Name</b><br />
@Html.HiddenFor(m => m.Id)
@Html.HiddenFor(m => m.Name)
@Html.EditorFor(m => m.StudentEntries)
`Student.cshtml` should look like this:
@model Student
@Html.HiddenFor(m => m.Id)
@Html.HiddenFor(m => m.Name)
@Html.DisplayFor(m => m.Name): @Html.EditorFor(m => m.Grade)
<br />
That's it. If you now build and run this application, putting a breakpoint on the `POST` index action, you'll see the model is correctly populated.
So, what are `EditorTemplates`, and their counterparts, `DisplayTemplates`? They allow you to create reusable portions of views, allowing you to organise your views a little more.
The great thing about them is the templated helpers, that is `Html.EditorFor` and `Html.DisplayFor`, are smart enough to know when they're dealing with a template for a collection. That means you no longer have to loop over the items, manually invoking a template each time. You also don't have to perform any `null` or `Count()` checking, because the helpers will handle that all for you. You're left with views which are clean and free of logic.
`EditorTemplates` also generate appropriate names when you want to POST collections to a controller action. That makes model binding to a list much, much simpler than generating those names yourself. There are times where you'd still have to do that, but this is not one of them.
Problem
I want to post a List of items to controller from Razor view , but i am getting a List of objects as null My class structre is Model: ``` List<Subjects> modelItem class Subjects { int SubId{get;set;} string Name{get;set;} List<Students> StudentEntires{get;set;} } class StudentEntires { int StudId{get;set;} string Name{get;set;} int Mark{get;set;} } ``` The model itself is a list of items and every items contain List of child items as well. Example model is a list of Subjects and every subject contains a List of Students, and i want to input mark for every student My View is like ``` @model IList<Subjects> @{ Layout = "~/Views/Shared/_Layout.cshtml"; } @using (Html.BeginForm()) { @Html.ValidationSummary(true) if (Model.Count > 0) { @for (int item = 0; item < Model.Count(); item++) { <b>@Model[item].Name</b><br /> @foreach (StudentEntires markItem in Model[item].StudentEntires) { @Html.TextBoxFor(modelItem => markItem.Mark) } } <p style="text-align:center"> <input type="submit" class="btn btn-primary" value="Update" /> </p> } } ``` And in controller ``` [HttpPost] public ActionResult OptionalMarks(int Id,ICollection<Subjects> model) { //BUt my model is null. Any idea about this? } ```