Reputation: 605
I have a view model which includes an ICollection field, something like this:
public class BusinessViewModel
{
...
public virtual ICollection<Subcategory> Subcategories { get; set; }
...
}
Then, in my actual view, I have a code segment like this:
<div class="form-group">
@Html.LabelFor(model => model.Subcategories, "Subcategory", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<span id="subcats">
</span>
</div>
</div>
Afterwards, I have an AJAX call in JS, which simply populates my subcats span field with checkbox items, and my AJAX call looks like this:
$("#CategoryID").change(function () {
$("#subcats").empty();
$.ajax({
type: 'POST',
url: '/Account/GetSubcategories',
dataType: 'json',
data: { id: $("#CategoryID").val() },
success: function (subcategories) {
$.each(subcategories, function (i, subcategory) {
$("#subcats").append('<input type="checkbox" name="' + subcategory.value + '" value="' + subcategory.id + '" />' + subcategory.value + '<br />');
});
},
error: function (ex) {
console.log('Failed to retrieve subcategories! ' + ex);
}
});
return false;
});
The point is that, it works all fine in the view, but when I check the items I wish and press the button in order to submit the form, I get an error message saying that a NullReferenceException occurred, when I reach the foreach statement of the below code in my controller:
public async Task<ActionResult> RegisterBusiness(BusinessViewModel model)
{
if (ModelState.IsValid)
{
...
foreach (var subcategory in model.Subcategories)
{
db.Entry(subcategory).State = EntityState.Unchanged;
user.Subcategories.Add(subcategory);
}
...
}
...
}
I guess this is due to the fact I don't assign the selected checkboxes to my model. Can someone, tell me how can I do it in my current setup? A code example where this is achieved would be highly welcomed.
EDIT: This is how my subcategory model looks like, at least important part of it:
public class Subcategory
{
public int SubcategoryID { get; set; }
public string SubcategoryName { get; set; }
}
And also according to the answer below my view now includes this:
<div class="form-group">
@Html.LabelFor(model => model.Subcategories, "Subcategory", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.ListBoxFor(model => model.SelectedSubcategories, Model.SubcategoryList)
</div>
</div>
I get a NullReference error again in the ListBoxFor part.
Upvotes: 1
Views: 668
Reputation:
Your SubCategories
are null because you are not posting back an values relating to SubCategories
. The following example is based on using a multi-select list to choose the sub categories (unchecked checkboxes do not post back, so using checkboxes is much more difficult)
First create a property in you view model that binds to the selected sub categories. You may also want a SelectList
property to render the initial sub categories to display
public int[] SelectedSubCategories { get; set; }
public SelectList SubCategoryList { get; set; }
and in the view, render the select
@Html.ListBoxFor(m => m.SelectedSubCategories, Model.SubCategoryList)
then modify your script
$("#CategoryID").change(function () {
$("#SelectedSubCategories").empty();
var url = '@Url.Action("GetSubcategories", "Account")';
$.get(url, { id: $("#CategoryID").val() }, function(subcategories) {
$.each(subcategories, function (i, subcategory) {
var option = $('<option></option>');
option.val(subcategory.id);
option.text(subcategory.value);
$("#SelectedSubCategories").append(option);
});
});
});
When posting back, the value of SelectedSubCategories
now contains the ID's of each selected sub category
public async Task<ActionResult> RegisterBusiness(BusinessViewModel model)
{
foreach (int ID in model.SelectedSubCategories)
{
Upvotes: 1