EJN EJN - 3 months ago 9
ASP.NET (C#) Question

View passes an empty model back to controller

I have a problem in ASP.NET. I made a list of checkboxes, and I want to post the selected values back to the controller.
Problem is, the model I recieve in my controller is null.

My controller:

[HttpPost]
// POST: /Checklist/index
public ActionResult Index(Zoeken systemencheckboxenss)
{
var zoekresultaat = _db.VW_Checks.AsQueryable();

if (systemencheckboxenss.Systemen != null)
{
foreach (var item in systemencheckboxenss.Systemen)
{
if(item.isSelected == true)
{
zoekresultaat = zoekresultaat.Where(sy => (sy.Systeemnaam.Contains(item.Systeemnaam)));
}
}
}

var systemen = _db.SYSTEEMs;
//Lijst van systemen en of het geslecteerd is
var systeemnamen = new List<CheckboxSysteem>();
//vult de lijst

foreach (var i in systemen)
{
systeemnamen.Add(new CheckboxSysteem
{
Systeemnaam = i.SYSTEEMNAAM,
isSelected = false,
});
}

var models = new Zoeken
{
VwChecks = zoekresultaat.ToList(),
Systemen = systeemnamen,
};

return View(models);
}


And here is my View:

@model UsabilityChecklistApp.ViewModels.Zoeken

@{
ViewBag.Title = "Index";
}

<h2>Checks</h2>

@using (Html.BeginForm("Index", "Checklist", Model, FormMethod.Post))
{
<table class="table">
@foreach (var item in Model.Systemen)
{
<tr>
<td>
@Html.HiddenFor(modelItem => item.Systeemnaam)
@Html.HiddenFor(modelItem => item.isSelected)
@Html.DisplayFor(modelItem => item.Systeemnaam)
</td>
<td>
@Html.CheckBoxFor(modelItem => item.isSelected)
</td>

</tr>
}
</table>
<input type="submit" value="Zoek" />
}


And ofcourse my ViewModel:

using System.Web;
using UsabilityChecklistApp.Models;

namespace UsabilityChecklistApp.ViewModels
{
public class Zoeken
{
public List<VW_Checks> VwChecks { get; set; }
public List<CheckboxSysteem> Systemen { get; set; }
}

public class CheckboxSysteem
{
public string Systeemnaam { get; set; }
public bool isSelected { get; set; }
}
}


If I did not provide information to solve the problem, please ask for it.

EDIT:
I must use checkboxes, no other options possible

Answer

Parameters are routed using their name by default so it is trying to map your model to a parameter named Model. When you're submitting a form things are usually handled for you though so you don't need to worry about the parameter name as the Model Binder will take care of the mapping.

All you need to do is remove the Model parameter from the form so the model binder can take care of it all for you

@using (Html.BeginForm("Index", "Checklist", FormMethod.Post))}

To get the list to map properly you'll need to changed your cshtml like so:

<table class="table">
    @for(int i = 0; i < Model.Systemen.Count; i++)
    {
        <tr>
            <td>
                @Html.HiddenFor(modelItem => modelItem.Systemen[i].Systeemnaam)
                @Html.DisplayFor(modelItem => modelItem.Systemen[i].Systeemnaam)
            </td>
            <td>
                @Html.CheckBoxFor(modelItem => modelItem.Systemen[i].isSelected)
            </td>

        </tr>
    }
</table>

The HTML helpers use expressions to generate the correct markup, specifically the names which need to have the index in order to map correctly. If you use a foreach the expression doesn't contain an index so the name generated is incorrect and can't be mapped back on to the list. There is a superb answer to a similar question which gives a much better description of this than I ever could.

I've give all of this a quick test and it is working once the Model parameter is removed from BeginForm.