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? } ```

Original source