1

I'm trying to obtain list of Id values for which user checked a checkbox. Here's the model:

using System.Collections.Generic;

namespace TestWebApplication3.Models
{
    public class TestViewModel
    {
        public IEnumerable<InnerViewModel> ModelData { get; set; }

        public class InnerViewModel
        {
            public int Id { get; set; }

            public bool Checked { get; set; }
        }
    }
}

Controller:

using System.Web.Mvc;
using TestWebApplication3.Models;

namespace TestWebApplication3.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var test = new TestViewModel();

            test.ModelData = new[]
            {
                new TestViewModel.InnerViewModel {Id = 10},
                new TestViewModel.InnerViewModel {Id = 20},
                new TestViewModel.InnerViewModel {Id = 30},
                new TestViewModel.InnerViewModel {Id = 40}
            };

            return View(test);
        }

        [HttpPost]
        public string TestAction(TestViewModel model)
        {
            string s = "";
            foreach (TestViewModel.InnerViewModel innerViewModel in model.ModelData)
            {
                if (innerViewModel.Checked)
                    s += innerViewModel.Id + " ";
            }
            return s;
        }
    }
}

And the View:

@model TestWebApplication3.Models.TestViewModel

@using (Html.BeginForm("TestAction", "Home"))
{
    <ol>
        @foreach (var testData in Model.ModelData)
        {
            <li>
                @Html.HiddenFor(m => testData.Id)
                @Html.CheckBoxFor(m => testData.Checked)
            </li>
        }
    </ol>

    <input type="submit"/>
}

So I'm displaying a list of InnerViewModel objects (created in Index action) as checkboxes. When user submits the form, I'd like to somehow obtain the list of Id values which are "checked" in TestAction method. But the returning model is always null.

In the application I'm making there are many more properties to the model, therefore it's important that the list of InnerViewModel objects is nested in the TestViewModel. I also don't want to use third party solution like MvcCheckBoxList, as it seems to me to be an overkill for such a simple task.

Can anyone explain to me what is missing for this to work?

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Piotr
  • 572
  • 5
  • 22

4 Answers4

1

I slightly changed your code to make it working -

ViewModel -

public class TestViewModel
{
    public List<InnerViewModel> ModelData { get; set; }
    public class InnerViewModel
    {
        public int Id { get; set; }
        public bool Checked { get; set; }
    }
}

Controller -

    public ActionResult Index()
    {
        var test = new TestViewModel();

        test.ModelData = new List<TestViewModel.InnerViewModel>()
        {
            new TestViewModel.InnerViewModel {Id = 10},
            new TestViewModel.InnerViewModel {Id = 20},
            new TestViewModel.InnerViewModel {Id = 30},
            new TestViewModel.InnerViewModel {Id = 40}
        };

        return View(test);
    }

    public string TestAction(TestViewModel model)
    {
        string s = "";
        foreach (TestViewModel.InnerViewModel innerViewModel in model.ModelData)
        {
            if (innerViewModel.Checked)
                s += innerViewModel.Id + " ";
        }
        return s;
    }

View -

@model MVC.Controllers.TestViewModel

@using (Html.BeginForm("TestAction", "Home"))
{
    <ol>
        @for (int i = 0; i < Model.ModelData.Count() ; i++ )
        {
            <li>
                @Html.HiddenFor(m => m.ModelData[i].Id)
                @Html.CheckBoxFor(m => m.ModelData[i].Checked)
            </li>
        }
    </ol>

    <input type="submit" />
}
ramiramilu
  • 17,044
  • 6
  • 49
  • 66
0

You need to understand how the model binder works. Simple once you understand that.

MVC Binding to checkbox

Community
  • 1
  • 1
Tom Styles
  • 1,098
  • 9
  • 20
0

Complex object require indexing in order for the model binder to pick them up.

Change it to this so the model binder will pick them up:

    @for (int i = 0; i < Model.ModelData.Count; i++)
    {
        <li>
            @Html.HiddenFor(m => Model.ModelData[i].Id)
            @Html.CheckBoxFor(m => Model.ModelData[i].Checked)
        </li>
    }

This is a good article explaining some of the gotchas in model binding.

http://msdn.microsoft.com/en-us/magazine/hh781022.aspx

hutchonoid
  • 32,982
  • 15
  • 99
  • 104
  • Changing a foreach to a for doesn't really make any difference in the grand scheme of things, let alone really make a difference in fixing this issue... They both do the same thing in the end. – IyaTaisho Jan 27 '14 at 18:07
  • 1
    @IyaTaisho that is incorrect. You MUST use a for instead of a foreach for the model binder to function correctly in this case. – Michael Dunlap Jan 27 '14 at 18:17
  • @digitalid thanks agree, I got down voted too. Unbelievable. :) – hutchonoid Jan 27 '14 at 18:40
  • @lyataisho yes you do need a for, the link you sent is for an editorfor which behaves differently. Read the link in the article on my answer to understand it. – hutchonoid Jan 27 '14 at 18:44
  • I did read it first, hence why I was puzzled why you didn't bother to use a CheckBoxFor. It was much simpler than using a for or foreach loop. But each their own I suppose. – IyaTaisho Jan 27 '14 at 19:01
  • @lyataisho have you read my answer before down voting? It is using a checkboxfor? – hutchonoid Jan 27 '14 at 19:04
  • 1
    @hutchonoid This works indeed. I accepted ramiramilu's answer because his was first, but it was essentially the same so I can confirm that your solution works. Thanks. – Piotr Jan 28 '14 at 08:06
  • @caleb9 Nice one, I thought I was going mad. :) – hutchonoid Jan 28 '14 at 08:52
0

One thought would be to use a CheckBoxFor control. It saves you a whole lot of trouble in the end in finding what is checked and what isn't. I built a RadioButtonListFor one before and it wasn't very difficult. In fact, here is a link to use on it.

Create MVC3 CheckBoxFor from List and getting the list back (With updated values) on Post

Community
  • 1
  • 1
IyaTaisho
  • 863
  • 19
  • 42