0

I've read and implemented many answers here and around the web but had no luck..

My model looks something like this:

public class CampaignModel : BaseModel
{
    .
    .
    public List<TreeItem> Countries { get; set; }
    .
    .
}

In the view i have:

@foreach (var country in Model.Countries.Select((value,i)=> new {i, value}))
{
<input type="checkbox" name="campaign.Countries[@country.i].Id" value="@country.value.Id" @(country.value.IsSelected ? "checked=\"checked\"" : "") />
}

At the action I have:

[HttpPost]
public ActionResult UpdateTargeting(CampaignModel campaign)
{
    return View(campaign);
}

But the 'Countries' property turns out null.

What am I doing wrong? Thank you

Leo
  • 900
  • 1
  • 11
  • 26

2 Answers2

3

First I think you need to use Model instead of campaign in the name attributes, like below:

name="@Model.Countries[@country.i].Id"

And right now your foreach loop will generate the html code like below:

<input type="checkbox" name="1" value="1"/>
<input type="checkbox" name="2" value="2" checked=""checked""/>

With above code, the model binding will not work, that's why you got null values when you submitted the form. You need something like below:

<input id="Countries_0__IsSelected" name="Countries[0].IsSelected" type="checkbox" value="true"/>
<input name="Countries[0].IsSelected" type="hidden" value="false"/>
<input checked="checked" id="Countries_1__IsSelected" name="Countries[1].IsSelected" type="checkbox" value="true"/>
<input name="Countries[1].IsSelected" type="hidden" value="false"/>

So I suggest you to use Razor syntax, like below:

foreach (var country in Model.Countries.Select((value,i)=> new {i, value}))
{
      @Html.CheckBoxFor(m => m.Countries[@country.i].IsSelected )    
}
Lin
  • 15,078
  • 4
  • 47
  • 49
  • Thanks Lin, what you wrote helped me come out with the solution below! – Leo Jan 01 '14 at 15:13
  • 1
    Note to others who tried this: doesn't work for IEnumerable or ICollection of Countries, only on List or Array – OzBob Dec 05 '14 at 02:46
0

I ended up adding 2 more hidden fields to identify the exact checkbox.

The TreeItem lolks like this:

public class TreeItem
{
    public int Id { get; set; }
    public string Title { get; set; }
    public bool IsSelected { get; set; }
    public List<TreeItem> Leafes { get; set; }

    public TreeItem() { }

    public TreeItem(int id, string title, bool selected = false, List<TreeItem> leafes = null)
    {
        Id = id;
        Title = title;
        IsSelected = selected;
        Leafes = leafes;
    }
}

The full solution looks like this (I demonstrate a a 2 level hierarchy):

<ul id="devicesList">
    @foreach (var brand in Model.DeviceBrands.Select((value, i) => new { i, value }))
    { 
                    <li>
                        @Html.CheckBoxFor(m => m.DeviceBrands[@brand.i].IsSelected)
                        @Html.HiddenFor(m => m.DeviceBrands[@brand.i].Id)
                        @Html.HiddenFor(m => m.DeviceBrands[@brand.i].Title)
                        <label><b>@brand.value.Title</b></label>
                        <ul>
                            @foreach (var deviceModel in brand.value.Leafes.Select((value, j) => new { j, value }))
                            {
                                <li>
                                    @Html.CheckBoxFor(m => m.DeviceBrands[@brand.i].Leafes[@deviceModel.j].IsSelected)
                                    @Html.HiddenFor(m => m.DeviceBrands[@brand.i].Leafes[@deviceModel.j].Id)
                                    @Html.HiddenFor(m => m.DeviceBrands[@brand.i].Leafes[@deviceModel.j].Title)
                                    <label>@deviceModel.value.Title</label>
                                </li>
                            }
                        </ul>
                    </li>
    }
</ul>

Thanks Lin!

Leo
  • 900
  • 1
  • 11
  • 26