Search code examples
c#htmlasp.net-mvcselectlistitem

MVC List<SelectListItem> with groups creating optgroup for each item


I'm having an issue where I'm creating a List<SelectListItem> with optgroups but instead of creating an optgroup per group of SelectListItem it's creating a new SelectListGroup per SelectListItem. It's got me a bit confused because there aren't any duplicated SelectListGroup's in my code.

Here is an example:

Expected Result:

<select datatag="data-States=''" class="form-control filter-select" data-multi-select="" id="States" multiple="multiple" name="States">
    <optgroup label="MA">
    <option value="01602">01602</option>
    <option value="02743">02743</option>
    <option value="01107">01107</option>
    </optgroup>
    </select>

Actual Result:

<select datatag="data-States=''" class="form-control filter-select" data-multi-select="" id="States" multiple="multiple" name="States">
<optgroup label="MA">
<option value="01602">01602</option>
</optgroup>
<optgroup label="MA">
<option value="02743">02743</option>
</optgroup>
<optgroup label="MA">
<option value="01107">01107</option>
</optgroup>
</select>

Method:

 public ManifestFilterDropDownItem ReturnManifestFilterDataBasedOnTotalDataSet(IEnumerable<ManifestTableItem> data, bool isUserASR) {
            IEnumerable<SelectListGroup> stateGroups = data.Select(x => x.AddrState.ToUpper()).Distinct().Select(x => new SelectListGroup() {
                Name = x
            });

            IList<SelectListItem> stateZipSelectListItems = data.GroupBy(x => x.AddrZip).Select(x => new SelectListItem() {
                Text = string.IsNullOrWhiteSpace(x.Key) ? "Empty" : x.Key,
                Value = string.IsNullOrWhiteSpace(x.Key) ? "" : x.Key,
                Group = stateGroups.Where(y => y.Name == data.Where(p => p.AddrZip == x.Key).First().AddrState.ToUpper()).Single()
            }).OrderBy(x => x.Group.Name).ToList();


            var manifestItem = new ManifestFilterDropDownItem {
                States = stateZipSelectListItems
            return manifestItem;
        }

ViewModel:

using System.Collections.Generic;
using System.Web.Mvc;

namespace FSVendor.Models.Manifest {
    public class ManifestFilterViewModel {
        public ManifestFilterViewModel() {

        }

        public string Name { get; set; }
        public string DataTag => $"data-{Name}=''";
        public IEnumerable<SelectListItem> SelectListItems { get; set; }
    }
}

View:

@model FSVendor.Models.Manifest.ManifestFilterViewModel

<label>States:</label>
@Html.DropDownList(Model.Name, Model.SelectListItems, new { @class = "form-control filter-select", data_multi_select = "", multiple = "multiple", @Model.DataTag })

Solution

  • You query is creating a new SelectListGroup for each SelectListItem, even though each SelectListGroup has the same value.

    Modify the query to group the data, and then create a new SelectListGroup for each group

    // Initialize model
    var model = new ManifestFilterDropDownItem
    {
        States = new List<SelectListItem>
    }
    var states = data.GroupBy(x => x.AddrState); // group by state
    foreach (var group in states)
    {
        // Create a SelectListGroup
        var optionGroup = new SelectListGroup() { Name = group.Key };
        // Add SelectListItem's
        foreach (var item in group)
        {   
            model.States.Add(new SelectListItem()
            {
                Value = item.AddrZip,
                Text = item.AddrZip,
                Group = optionGroup
            })
        }
    }
    return model;
    

    Alternatively, you can use one of the overloads of the SelectList constructor

    var model = new ManifestFilterDropDownItem
    {
        States = new SelectList(data, "AddrZip", "AddrZip", "AddrState", null, null)
    };
    return model;
    

    As a side note, do not use DropDownList() to create a <select multiple>. You need to use ListBoxFor() in order for 2-way model binding to work. Refer Why does the DropDownListFor lose the multiple selection after Submit but the ListBoxFor doesn't?