trevorgk
trevorgk

Reputation: 1466

Problems model binding nested list in ASP.NET MVC

I am trying to bind a view to a model which contains a list in a list. Naturally I would prefer to use out of the box model binding. Having spent some time on it yesterday I found a workaround which is really a hack and I would like to correct this. The basic structure of my models are as follows:

public class MyMPIModel
{
    public List<ScoreInfo> ScoreInfo { get; set; }
}

public class ScoreInfo 
{
    public int ScorePrefId { get; set; }
    public List<Category> Categories { get; set; }
}

public class Category
{
    public int Id;
    public string Name;
    public bool Checked;
}

The view InterestCategories.cshtml contains the following form:

@using (Html.BeginForm())
{
    for (var i = 0; i < Model.ScoreInfo.Count; i++)
    {
        @Html.EditorFor(x => x.ScoreInfo[i])
    }
}

The editor template ScoreInfo.cshtml:

@Html.HiddenFor(x => x.ScorePrefId)
<div class="preferences-block">
    @for (var i = 0; i < Model.Categories.Count; i++)
    {
        @Html.EditorFor(x => x.Categories[i])
    }
</div>

Finally the editor template Category.cshtml:

@Html.HiddenFor(x => x.Id)
@Html.HiddenFor(x => x.Name)
<label>
    @Html.CheckBoxFor(x => x.Checked, new { @class = "check"})
    <span>@Model.Name</span>
</label>

Inspecting the form using firebug I can see that all the hidden fields have been populated. Also when I submit the form, Fiddler shows the correct data. Here is a sample:

ScoreInfo[0].Categories[1].Id   2
ScoreInfo[0].Categories[1].Name Managing Money
ScoreInfo[0].Categories[1].Checked  false

However, when I post to the controller, set a breakpoint and inspect the model, the list of ScoreInfo objects have been populated but the lists of Category objects inside the ScoreInfo object have not.

I have the following POST action in my controller:

[HttpPost]
public ActionResult InterestCategories(MyMPIModel model, FormCollection form)
{
    ...

    //  model would not bind forcing me to populate via form collection
    for (var i = 0; i < model.ScoreInfo.Count; i++)
    {
        ...
        for (var j = 0; j < scoreInfo.Categories.Count; j++)
        {
                var category = scoreInfo.Categories[j];

                var prefix = "ScoreInfo[" + i + "].Categories[" + j + "]";

                category.Name = form[prefix + ".Name"];

                var sId = form[prefix + ".Id"];
                if (sId != null) category.Id = Int32.Parse(sId);

                var sChecked = form[prefix + ".Checked"];
                if (sChecked != null) category.Checked = sChecked.Contains("true");
        }
    }
}

Upvotes: 3

Views: 7659

Answers (1)

thepirat000
thepirat000

Reputation: 13114

You have to use Properties instead of Fields in your Category class:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool Checked { get; set; }
}

Upvotes: 7

Related Questions