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?

4 Answers 4

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

Comments

0

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

MVC Binding to checkbox

Comments

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

8 Comments

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 that is incorrect. You MUST use a for instead of a foreach for the model binder to function correctly in this case.
@digitalid thanks agree, I got down voted too. Unbelievable. :)
@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 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.
|
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

Comments

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.