Search code examples
asp.net-mvc-5html.listboxfor

Check if ListBoxFor selectedValues is null before display in view?


I have a number of ListBoxFor elements on a form in edit mode. If there was data recorded in the field then the previously selected items are displaying correctly when the form opens. If the field is empty though an error is thrown as the items parameter cannot be null. Is there a way to check in the view and if there is data to use the ListBoxFor with the four parameters but if there isn't to only use three parameters, leaving out the selected items?

This is how I'm declaring the ListBoxFor:

@Html.ListBoxFor(model => model.IfQualityPoor, new MultiSelectList(ViewBag.IfPoor, "Value", "Text", ViewBag.IfQualityPoorSelected), new { @class = "chosen", multiple = "multiple" })  

I'm using the ViewBag to pass the ICollection which holds the selected items as the controller then joins or splits the strings for binding to the model field. The MultiSelectLists always prove problematic for me.


Solution

  • Your question isn't entirely clear, but you're making it way harder on yourself than it needs to be using ListBoxFor. All you need for either DropDownListFor or ListBoxFor is an IEnumerable<SelectListItem>. Razor will take care of selecting any appropriate values based on the ModelState.

    So, assuming ViewBag.IfPoor is IEnumerable<SelectListItem>, all you need in your view is:

    @Html.ListBoxFor(m => m.IfQualityPoor, (IEnumerable<SelectListItem>)ViewBag.IfPoor, new { @class = "chosen" })
    

    The correct options will be marked as selected based on the value of IfQualityPoor on your model, as they should be. Also, it's unnecessary to pass multiple = "multiple" in in your htmlAttributes param, as you get that just by using ListBoxFor rather than DropDownListFor.

    It's even better if you use a view model and then add your options as a property. Then, you don't have to worry about casting in the view, which is always a good way to introduce runtime exceptions. For example:

    public class FooViewModel
    {
        ...
    
        public IEnumerable<SelectListItem> IfQualityPoorOptions { get; set; }
    }
    

    Then, you set this in your action, before returning the view (instead of setting ViewBag). Finally, in your view:

    @Html.ListBoxFor(m => m.IfQualityPoor, Model.IfQualityPoorOptions, new { @class = "chosen" })
    

    Much simpler, and you'll never have any issues doing it that way.

    UPDATE (based on comment)

    The best way to handle flattening a list into a string for database storage is to use a special property for that, and then custom getter and setter to map to/from. For example:

    public string IfQualityPoor
    {
        get { return IfQualityPoorList != null ? String.Join(",", IfQualityPoorList) : null; }
        set { IfQualityPoorList = !String.IsNullOrWhiteSpace(value) ? value.Split(',').ToList() : null; }
    }
    
    [NotMapped]
    public List<string> IfQualityPoorList { get; set; }
    

    Then, you post to/interact with IfQualityPoorList, and the correct string will be set in the database automatically when you save.