Search code examples
blazor.net-8.0mudblazormudselect

Blazor / MudSelect: how to pre-select certain values from a multi-select dropdown


I have a MudBlazor MudSelect component on my Blazor (server-side rendering) page, which displays a list of departments from my school:

<MudSelect T="KeyText" id="idAddDept" @bind-SelectedValues="AdditionalDepartments" 
           MultiSelection=true ToStringFunc="x => x.Text" Delimiter=", ">
    @foreach (KeyText item in ListOfDepartments)
    {
        <MudSelectItem T="KeyText" Value="@item">@item.Value</MudSelectItem>
    }
</MudSelect>

The KeyText is a simple class:

public class KeyText
{
    public int Key { get; set; }
    public string Text { get; set; }
}

to hold my data from MS Dynamics - a numerical key value, associated with a textual representation of the department; 1001 - Dept. A, 1002 - Dept. B etc.

I can successfully load the list of all departments from Dynamics, and I store that in my component in a ListOfDepartments variable:

protected List<KeyText> ListOfDepartments { get; set; }

I also have another variable called AdditionalDepartments, which holds the currently selected departments:

protected IEnumerable<KeyText> AdditionalDepartments { get; set; }

I'm storing a comma-separated list of the numerical values of the selected "additional departments" in my database table:

1001,1004,1007

and when loading the object that's being worked on in this page, I parse this string value into a List<KeyText> values - based on the complete ListOfDepartments.

The trouble I'm facing right now is this: the multi-select dropdown properly shows all valid values from the ListOfDepartments, and I can select any number of them from the dropdown - but I cannot seem to initialize that display with those departments that have already been selected previously - those contained in AdditionalDepartments.

I had been reading about MudSelect having a Selected property on the MudSelectItem (in case the parent MudSelect is using multi selection - which it is, here in this case) - but I cannot get this to work - this code won't even compile:

<MudSelect T="KeyText" id="idAddDept" @bind-SelectedValues="AdditionalDepartments" 
           MultiSelection=true ToStringFunc="x => x.Text" Delimiter=", ">
    @foreach (KeyText item in ListOfDepartments)
    {
        <MudSelectItem T="KeyText" Value="@item" Selected="true">@item.Value</MudSelectItem>
    }
</MudSelect>

What am I missing here? I had hoped with the @bind-SelectedValues, those additional departments that have already been selected earlier would be properly displayed as such in the MudSelect dropdown - they are not. And I cannot seem to be able to find any way to mark those entries with a Selected moniker, either...


Solution

  • This one intrigued me: You seem to be jumping through hoops to get it to work. [And with 270K points you aren't a newbie].

    So I built a MRE to mimic [I hope] what you are trying to do.

    I soon discovered that the documentation on the subject is rather basic. Nice happy path simplistic example.

    After some experimenting, and raising an issue with MudBlazor to get some help, here's code that works.

    @page "/"
    @using System.Text
    @using System.Text.Json
    @using System.Text.RegularExpressions
    <PageTitle>Home</PageTitle>
    
    
    <MudText Typo="Typo.h3" GutterBottom="true">Hello, world!</MudText>
    <MudText Class="mb-8">Welcome to your new app, powered by MudBlazor and the .NET 8 Template!</MudText>
    
    <br />
    
    <MudSelect T="KeyText"
               @bind-SelectedValues="_selectedCountries"
               MultiSelectionTextFunc="GetMultiSelectionText"
               MultiSelection>
    
        @foreach (var item in _countries)
        {
            <MudSelectItem T="KeyText" Value="@item">@item.Text</MudSelectItem>
        }
        </MudSelect>
    
    @code {
        private IEnumerable<KeyText> _selectedCountries { get; set; } = new HashSet<KeyText>() { new(1, "UK"), new(3, "Spain") };
    
        private string GetMultiSelectionText(List<string> selectedValues)
        {
            // This is a list of ToString strings produced from the selected items
            // We've overridden ToString to produce json so we can easily serialize the list
            StringBuilder sb = new();
    
            foreach (var json in selectedValues)
            {
                var record = JsonSerializer.Deserialize<KeyText>(json);
                if (record is not null)
                    sb.Append($"{record.Text}, ");
    
            }
            return sb.ToString().Trim().Trim(',');
        }
    
        private IEnumerable<KeyText> _countries => new List<KeyText> {
            new(1, "UK"),
            new(2, "France"),
            new(3, "Spain"),
            new(4, "Portugal"),
            new(5, "Australia"),
            new(6, "Cook Islands"),
        };
    
        public record KeyText(int Key, string Text)
        {
            public override string ToString()
            {
                return JsonSerializer.Serialize(this); 
            }
        }
    }
    

    The basics are:

    1. I'm using records. Their value semantics makes equality checking easy.
    2. I've overridden ToString() on KeyText to produce a JSON object so we can deserialize it in the MultiSelectionTextFunc handler to create the display value.

    Thanks to @ScarletKuro at MudBlazor for the help.