3

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");
        }
    }
}

1 Answer 1

7

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; }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks and well spotted!! I was reusing another developer's models from a similarly malfunctioning part of the system. That would explain why.
What will happen if you remove in the middle of the collection value (say for example you have a dynamic grid to show it with inline delete button).is model binding still works or give error.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.