2

I have a list of information sources in a database that I need to pass to a view in MVC. I need an end user to be able to tick the sources of information that apply to their course.

I am able to successfully pass the view a list of information sources alongside check boxes using the following code.

public ViewResult CreateUpdateInfoSource(int ProgrammeId)
        {
            List<ProgInfoSourceModel> viewmodel = new List<ProgInfoSourceModel>();
            List<ProgInfoSourceDTO> myProgInfoDTOList = progInfoSourceService.AllInfoSources(); 
            if (myProgInfoDTOList.Count != 0)
            {
                foreach (var x in myProgInfoDTOList)
                {
                    ProgInfoSourceModel insert = new ProgInfoSourceModel();
                    insert.Selected = false;
                    insert.ProgrammeId = ProgrammeId;
                    insert.InfoSourceId = x.InfoSourceId;
                    insert.InfoSource = x.InfoSource;
                    insert.InfoReference = x.InfoReference;
                    insert.Rank = x.Rank;

                    viewmodel.Add(insert);
                }
            }

            return View(viewmodel);
        }

I am able to unpack this in the view just fine, however I am having real difficulty passing a the list back to my controller. I need to be able to loop through the list in my controller and see which ones do or don't apply so I can update the database.

My model looks like this:

namespace ProgrammeSpec.MVC.Models
{
    public class ProgInfoSourceModel
    {
        [DisplayName("Selected")]
        public bool Selected { get; set; }
        [DisplayName("Programme Id")]
        public int ProgrammeId { get; set; }
        [DisplayName("Info Source Id")]
        public int InfoSourceId { get; set; }
        [DisplayName("Info Source")]
        public String InfoSource { get; set; }
        [DisplayName("Reference")]
        public String InfoReference { get; set; }
        [DisplayName("Rank")]
        public int? Rank { get; set; }
    }
}

My View looks like this:

<html>
<head>
    <title>CreateUpdateInfoSource</title>
</head>
<body>

@using (Html.BeginForm())
{
    <table>
        <tr>
            <th>
                Selected
            </th>
            <th>
                ProgrammeId
            </th>
            <th>
                InfoSourceId
            </th>
            <th>
                InfoSource
            </th>
            <th>
                InfoReference
            </th>
            <th>
                Rank
            </th>
            <th></th>
        </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.CheckBoxFor(modelItem => item.Selected)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ProgrammeId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.InfoSourceId)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.InfoSource)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.InfoReference)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Rank)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
                @Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
            </td>
        </tr>
    }


    </table>
    <input type="submit" value="Update" />
}
</body>
</html>

and the controller that the view gets passed to looks like this: (snippet)

[HttpPost]
        public ActionResult CreateUpdateInfoSource(List<ProgInfoSourceModel> viewmodel)
        {
            if (ModelState.IsValid)
            {
                try
                {

The problem is the viewmodel is null. I understand this is probably because I've unpacked the list in the view so it is no longer a list but how can I access the values of the check boxes then?

The added complication is that the number of info sources will vary so I can't use a static form or list and give each one an Id...

This must be a fairly common problem with a simple solution, but I'm an MVC novice and I don't know how to get round this.

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160

1 Answers1

0

Try using an editor template (Here's another SO that answers that question How to create custom editor/display templates in ASP.NET MVC 3?), for your ProgInfoSourceModel and then simply use Html.EditorFor(m => m.Model) on the View.

When you use the foreach loop, each checkbox is getting the same input name - and so is not actually submitting the correct data back.

If you go the editor template route, and making MVC do the hard work of iterating through the IEnumerable - it will create inputs with names like 'item[0].Selected' - which the model binder then correctly deserialized back into a list.

Phil Haack also blogged a fantastic walkthrough of exactly this scenario way back in 2008: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Community
  • 1
  • 1
Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • Thank you for this, it has taken me a few hours and I have finally cracked it. Had to do quite a bit of reading around but this pointed me in the right direction. I'm glad you didn't tell me exactly how to do it as it has meant I have learned a lot working out the answer for myself and actually learned something! cheers –  Mar 07 '12 at 13:53
  • @D.Mac - I did consider spoon-feeding; but I judged by the effort you put into the question that you were more interested in learning :) – Andras Zoltan Mar 07 '12 at 14:34