How get checked values of checkbox list in asp.net mvc 3 with strongly typed view?

Stack0verflow picture Stack0verflow · May 18, 2012 · Viewed 9.4k times · Source

Keywords: asp.net mvc 3, checkbox list, strongly typed view.

So, please don't suggest me to use ViewBag or formcollection.

I am not new to web form development, but I am fairly new to asp.net MVC.

Suppose on a registration page, I have something like this:

A very simple registration form

What would the view model look like? Does the following look right?

public class RegistrationViewModel

{

// To store the selected state ID upon postback.
public int StateId {get; set; }  

// A list of states to populate the dropdown.
public Dictionary<int, string> States {get; set; } 

// To store the list of insurance companies the user has used before.
public int[] PriorInsuranceCompanies {get; set; } 

// A list of insurance companies to populate the check box list.
public Dictionary<int, string> InsuranceCompanies {get; set; }

}

I know how to create the view like I've shown in the screen shot, but I am not sure how in my controller I should capture the view model values of user input. Remember I prefer a strongly typed view. How does the mvc framework tell which is which? Do you have a minimal working sample to share (preferably in a complete runnable project)? Thank you.

Answer

Iridio picture Iridio · May 18, 2012

The approach you are using is correct. I agree with you in using always viewmodels and never ViewBag.

In your viewmodel you should change your dictionary to MultiSelectList so you can have the selected values also.

public IList<int> PriorInsuranceCompaniesSelected { get; set; }
public MultiSelectList PriorInsuranceCompanies { get; set; }

You then map the first field if some Ids are already selected (info that you get when loading data from your repo for example) and the second with all the values.

From your controller in the Get part (just some code as an example):

  model.PriorInsuranceCompaniesSelected = new List<int>();
  var companies = repository.GetPriorInsuranceCompanies();
  //add to your PriorInsuranceCompaniesSelected the values already checked from your entity
  var entity = repository.GetEntityBy(id);
  if (entity.PriorInsuranceCompanies != null)
    foreach (var item in entity.PriorInsuranceCompanies)
      model.PriorInsuranceCompaniesSelected.Add(item.Id);

  var select = (from s in companies select new { Id = s.Id, Name = s.Name }).OrderBy(x => x.Name); //.ToList;
  model.PriorInsuranceCompanies = new MultiSelectList(select, "Id", "Name", model.PriorInsuranceCompaniesSelected);

Then in your Html you will have an output like this

@foreach (var item in Model.PriorInsuranceCompanies)
{
   <label for="@item.Value" class="check">
   <input type="checkbox" id="@item.Value" name="PriorInsuranceCompaniesSelected" value="@item.Value" @(item.Selected ? "checked" : "") />@item.Text</label>
}

On post, the ModelBinder will map the correct objects to your model automagically. You simply have to check values in model.PriorInsuranceCompaniesSelected

[HttpPost]
public ActionResult MyForm(MyViewModel model)
{
  if (ModelState.IsValid)
  {
    try
    {
      //your mapping code or whatever...

      //You do your things with the selected ids..
      if (model.PriorInsuranceCompaniesSelected != null && model.PriorInsuranceCompaniesSelected.Count > 0)
        entity.PriorInsuranceCompanies = repository.GetCompaniesBy(model.PriorInsuranceCompaniesSelected);
      else
        entity.PriorInsuranceCompanies = new List<Comapny>();
      repository.Save(entity);

      return RedirectToAction("Index");
    }
    catch (RulesException ex)
    {
      ex.CopyTo(ModelState);
    }
    catch
    {
      ModelState.AddModelError("", "My generic error taken form a resource");
    }
  }

  //rehydratates the list in case of errors
  //....
  return View(model);
}

This should give you an idea of what to do. I hope it helps