true-perfect-code
Version: 1.1.69

P11SelectMultiple Component

The P11SelectMultiple component provides a multi-selection dropdown list, wrapping the native HTML <select multiple> element. It enables users to select multiple options from a predefined list, integrated with Bootstrap's .form-select class for consistent styling and offering flexible data binding and accessibility features.
Note: Similar to P11Select, options can be populated via an Items collection with expressions/fields, or manually through ChildContent. Ensure your binding target (@bind-Value) for this component is an ICollection<TValue> (e.g., List<string> or HashSet<int>) to correctly handle multiple selections.


P11SelectMultiple Component Examples

These examples demonstrate various configurations and functionalities of the P11SelectMultiple component, showcasing its flexibility for different use cases.

1. Standard Usage with List of Objects (Multiple Selection)

Selecting multiple names from a list of Person objects.

Selected Person IDs: Nothing selected

Implementation

<h4 class="mb-3">1. Standard Usage with List of Objects (Multiple Selection)</h4>
<p>Selecting multiple names from a list of <code>Person</code> objects.</p>
<div class="mb-4">
    <label for="multiPersonSelect" class="form-label">Select Persons (Multiple Selection):</label>
    <P11SelectMultiple TItem="Person" TValue="int"
                       Id="multiPersonSelect"
                       @bind-Value="selectedPersonIds"
                       Items="people2"
                       ItemValueExpression="p => p.Id"
                       ItemTextExpression="p => p.Name"
                       PlaceholderText="-- No selection --"
                       OnChanged="OnMultiplePersonsChanged"
                       CssClass="form-select-lg"
                       AriaLabel="Select multiple persons dropdown">
    </P11SelectMultiple>
    <p class="mt-2">
        Selected Person IDs:
        <strong>
            @(selectedPersonIds != null && selectedPersonIds.Any()
                                ? string.Join(", ", selectedPersonIds)
                                : "Nothing selected")
        </strong>
    </p>
    @if (!string.IsNullOrEmpty(multiPersonChangedMessage))
    {
        <p class="text-info">@multiPersonChangedMessage</p>
    }
</div>
@code {
    // Person Model (often in a separate .cs file, e.g., Models/Person.cs)
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string City { get; set; } = string.Empty;
    }

    // Data for examples
    private List<Person> people2 = new List<Person>
    {
        new Person { Id = 1, Name = "Alice", City = "New York" },
        new Person { Id = 2, Name = "Bob", City = "London" },
        new Person { Id = 3, Name = "Charlie", City = "Paris" },
        new Person { Id = 4, Name = "Diana", City = "Berlin" }
    };

    // Properties for binding P11SelectMultiple
    private ICollection<int> selectedPersonIds = new List<int>(); // List<int> for multiple selection
    private string? multiPersonChangedMessage;

    // Event handlers for P11SelectMultiple
    private void OnMultiplePersonsChanged(ICollection<int> newPersonIds)
    {
        multiPersonChangedMessage = $"Personen geändert zu: {string.Join(", ", newPersonIds ?? new List<int>())}";
        Console.WriteLine(multiPersonChangedMessage);
    }
}


2. Usage with Enum (Multiple Selection)

Selecting multiple priorities from an enum.

Selected Priorities: Nothing selected

Implementation

<h4 class="mb-3">2. Usage with Enum (Multiple Selection)</h4>
<p>Selecting multiple priorities from an <code>enum</code>.</p>
<div class="mb-4">
    <label for="multiPrioritySelect" class="form-label">Select Priorities (Multiple Selection):</label>
    <P11SelectMultiple TItem="Priority" TValue="Priority"
                       Id="multiPrioritySelect"
                       @bind-Value="selectedPriorities"
                       Items="Enum.GetValues<Priority>()"
                       ItemValueExpression="p => p"
                       ItemTextExpression="p => p.ToString()"
                       PlaceholderText="-- Select Priorities --"
                       AriaLabel="Select multiple priorities dropdown">
    </P11SelectMultiple>
    <p class="mt-2">
        Selected Priorities:
        <strong>
            @(selectedPriorities != null && selectedPriorities.Any()
                                ? string.Join(", ", selectedPriorities)
                                : "Nothing selected")
        </strong>
    </p>
</div>
@code {
    // Enum definition (if not already globally defined)
    public enum Priority
    {
        Low,
        Medium,
        High,
        Critical
    }

    // Properties for binding P11SelectMultiple
    private ICollection<Priority> selectedPriorities = new List<Priority>(); // List<Priority> for multiple enum selection
}


3. Manual Options (ChildContent) (Multiple Selection)

Direct rendering of <option> tags within the component for multiple selection.

1 item selected

Selected Cities: MUN

Implementation

<h4 class="mb-3">3. Manual Options (ChildContent) (Multiple Selection)</h4>
<p>Direct rendering of <code>&lt;option&gt;</code> tags within the component for multiple selection.</p>
<div class="mb-4">
    <label for="multiCitySelect" class="form-label">Select Cities Manually (Multiple Selection):</label>
    <P11SelectMultiple TItem="string" TValue="string"
                       Id="multiCitySelect"
                       @bind-Value="selectedCities"
                       PlaceholderText="Select cities manually"
                       AriaLabel="Select multiple cities dropdown">
        <option value="BER">Berlin</option>
        <option value="MUN">Munich</option>
        <option value="HAM">Hamburg</option>
        <option value="COL">Cologne</option>
        <option value="FRA">Frankfurt</option>
    </P11SelectMultiple>
    <p class="mt-2">
        Selected Cities:
        <strong>
            @(selectedCities != null && selectedCities.Any()
                                ? string.Join(", ", selectedCities)
                                : "Nothing selected")
        </strong>
    </p>
</div>
@code {
    private ICollection<string> selectedCities = new List<string> { "MUN" }; // Pre-selected for manual example
}


4. Disabled Select (Multiple Selection)

A multi-selection field that is not interactive.

2 items selected

Disabled Selection: 1, 3

Implementation

<h4 class="mb-3">4. Disabled Select (Multiple Selection)</h4>
<p>A multi-selection field that is not interactive.</p>
<div class="mb-4">
    <label for="disabledMultiSelect" class="form-label">Disabled Multiple Selection:</label>
    <P11SelectMultiple TItem="Person" TValue="int"
                       Id="disabledMultiSelect"
                       @bind-Value="disabledSelectedPersonIds"
                       Items="people2"
                       ItemValueExpression="p => p.Id"
                       ItemTextExpression="p => p.Name"
                       IsDisabled="true"
                       PlaceholderText="Selection disabled"
                       AriaLabel="Disabled multiple selection dropdown">
    </P11SelectMultiple>
    <p class="mt-2">
        Disabled Selection:
        <strong>
            @(disabledSelectedPersonIds != null && disabledSelectedPersonIds.Any()
                                ? string.Join(", ", disabledSelectedPersonIds)
                                : "Nothing selected")
        </strong>
    </p>
</div>
@code {
    // Person Model (often in a separate .cs file, e.g., Models/Person.cs)
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string City { get; set; } = string.Empty;
    }

    // Data for examples
    private List<Person> people2 = new List<Person>
    {
        new Person { Id = 1, Name = "Alice", City = "New York" },
        new Person { Id = 2, Name = "Bob", City = "London" },
        new Person { Id = 3, Name = "Charlie", City = "Paris" },
        new Person { Id = 4, Name = "Diana", City = "Berlin" }
    };
           

    // Properties for binding P11SelectMultiple
    private ICollection<int> disabledSelectedPersonIds = new List<int> { 1, 3 }; // Pre-selected for disabled example
}


5. Validation in EditForm (Multiple Selection)

Demonstrates integration with Blazor's EditForm and validation mechanisms for multiple selection.

  • Mindestens 1 Priorität(en) auswählen.
Validierung: Mindestens 1 Person auswählen (MinLength)
Validierung: Collection darf nicht null sein (Required)
Mindestens 1 Priorität(en) auswählen.
Validierung: Mindestens 1, maximal 3 Items (Custom Validation)
Debug Information:

Validation Status: Warten auf Absenden...

Selected Person IDs: 0 Items

Selected Priorities: 0

Complex Priorities: 0 Items



AOT warning: An example of manual options with short as TValue. Since short is not explicitly handled in the component's parsing logic, it falls back to Convert.ChangeType, which should trigger an AOT/Trimming warning in the user interface.

Selected short numbers: Nothing selected

Implementation

<h4 class="mb-3">5. Validation in EditForm (Multiple Selection)</h4>
<p>Demonstrates integration with Blazor's <code>EditForm</code> and validation mechanisms for multiple selection.</p>
<div class="mb-4">
    <EditForm Model="validationModelMultiple" OnValidSubmit="HandleValidSubmit3" OnInvalidSubmit="HandleInvalidSubmit3">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <div class="mb-3">
            <label for="requiredMultiPersonSelect" class="form-label">Required Field Persons (Multiple Selection):</label>
            <P11SelectMultiple TItem="Person" TValue="int"
                               Id="requiredMultiPersonSelect"
                               @bind-Value="validationModelMultiple.SelectedRequiredPersonIds"
                               Items="people2"
                               ItemValueExpression="p => p.Id"
                               ItemTextExpression="p => p.Name"
                               PlaceholderText="-- Please select --"
                               AriaLabel="Required Persons Dropdown">
            </P11SelectMultiple>
            <ValidationMessage For="@(() => validationModelMultiple.SelectedRequiredPersonIds)" />
        </div>

        <div class="mb-3">
            <label for="requiredMultiPrioritySelect" class="form-label">Required Field Priorities (Multiple Selection):</label>
            <P11SelectMultiple TItem="Priority" TValue="Priority"
                               Id="requiredMultiPrioritySelect"
                               @bind-Value="validationModelMultiple.SelectedRequiredPriorities"
                               Items="Enum.GetValues<Priority>()"
                               ItemValueExpression="p => p"
                               ItemTextExpression="p => p.ToString()"
                               PlaceholderText="-- Please select --"
                               AriaLabel="Required Priorities Dropdown">
            </P11SelectMultiple>
            <ValidationMessage For="@(() => validationModelMultiple.SelectedRequiredPriorities)" />
        </div>

        <button type="submit" class="btn btn-primary">Submit Form</button>
        <p class="mt-2">Validation Status: <strong>@validationStatus3</strong></p>
    </EditForm>
</div>

<br />
<br />

<p>
    AOT warning: An example of manual options with <code>short</code> as <code>TValue</code>.
    Since <code>short</code> is not explicitly handled in the component's parsing logic,
    it falls back to <code>Convert.ChangeType</code>, which should trigger an AOT/Trimming warning
    in the user interface.
</p>
<div class="mb-4">
    <label for="multiShortNumberSelect" class="form-label">Manually select short numbers:</label>
    <P11SelectMultiple TItem="short" TValue="short"
                       Id="multiShortNumberSelect"
                       @bind-Value="selectedShortNumbers"
                       PlaceholderText="Manually select short numbers"
                       AriaLabel="Dropdown for multiple selection of short numbers"
                       SkipValidation=false>
        <option value="10">Ten</option>
        <option value="20">Twenty</option>
        <option value="30">Thirty</option>
        <option value="40">Forty</option>
        <option value="50">Fifty</option>
    </P11SelectMultiple>
    <p class="mt-2">
        Selected short numbers:
        <strong>
            @(selectedShortNumbers != null && selectedShortNumbers.Any()
                                ? string.Join(", ", selectedShortNumbers)
                                : "Nothing selected")
        </strong>
    </p>
</div>
@* You may need the using *@
@* @using System.ComponentModel.DataAnnotations *@
@code {
    // Person Model (often in a separate .cs file, e.g., Models/Person.cs)
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; } = string.Empty;
        public string City { get; set; } = string.Empty;
    }

    // Data for examples
    private List<Person> people2 = new List<Person>
    {
        new Person { Id = 1, Name = "Alice", City = "New York" },
        new Person { Id = 2, Name = "Bob", City = "London" },
        new Person { Id = 3, Name = "Charlie", City = "Paris" },
        new Person { Id = 4, Name = "Diana", City = "Berlin" }
    };

    // Enum definition (if not already globally defined)
    public enum Priority
    {
        Low,
        Medium,
        High,
        Critical
    }

    // Validation Model for Multiple Select (often in a separate .cs file)
    public class ValidationTestModelMultiple
    {
        [Required(ErrorMessage = "Bitte wählen Sie mindestens eine Person aus.")]
        // For collections, Required checks if the collection is null.
        // To check if it's empty, you might need a custom validation attribute or check in OnValidSubmit.
        // However, a simple Required will catch a null collection.
        public ICollection<int>? SelectedRequiredPersonIds { get; set; } = new List<int>();

        [Required(ErrorMessage = "Bitte wählen Sie mindestens eine Priorität aus.")]
        public ICollection<Priority>? SelectedRequiredPriorities { get; set; } = new List<Priority>();
    }

    // Model for validation example
    private ValidationTestModelMultiple validationModelMultiple = new ValidationTestModelMultiple();
    private string validationStatus3 = "Warten auf Absenden...";
    private ICollection<short> selectedShortNumbers { get; set; } = new List<short>();

    private void HandleValidSubmit3()
    {
        validationStatus3 = "Formular ist GÜLTIG!";
        Console.WriteLine($"Validierung erfolgreich. Personen: {string.Join(", ", validationModelMultiple.SelectedRequiredPersonIds ?? new List<int>())}, Prioritäten: {string.Join(", ", validationModelMultiple.SelectedRequiredPriorities ?? new List<Priority>())}");
    }

    private void HandleInvalidSubmit3()
    {
        validationStatus3 = "Formular ist UNGÜLTIG!";
        Console.WriteLine("Validierung fehlgeschlagen.");
    }
}


6. OnChanged Event Example

This example demonstrates how to use the OnChanged event to react to changes in the selected values. The current selections are displayed immediately after a change.

Selected Languages (via OnChanged): None

Implementation

<h4 class="mb-3">6. OnChanged Event Example</h4>
<p>This example demonstrates how to use the <code>OnChanged</code> event to react to changes in the selected values. The current selections are displayed immediately after a change.</p>
<div class="mb-4">
    <label for="programmingLanguageSelect" class="form-label">Select Programming Languages:</label>
    <P11SelectMultiple TItem="string" TValue="string"
                       Id="programmingLanguageSelect"
                       @bind-Value="selectedProgrammingLanguages"
                       OnChanged="OnProgrammingLanguagesChanged"
                       Items="programmingLanguageOptions"
                       ItemValueExpression="lang => lang"
                       ItemTextExpression="lang => lang"
                       PlaceholderText="-- Choose languages --"
                       AriaLabel="Select programming languages dropdown">
    </P11SelectMultiple>
    <p class="mt-2">
        Selected Languages (via OnChanged):
        <strong>
            @(selectedProgrammingLanguages != null && selectedProgrammingLanguages.Any()
                                ? string.Join(", ", selectedProgrammingLanguages)
                                : "None")
        </strong>
    </p>
    @if (!string.IsNullOrEmpty(programmingLanguagesChangedMessage))
    {
        <p class="text-info">@programmingLanguagesChangedMessage</p>
    }
</div>
@code {
    // Properties for OnChanged event example
    private ICollection<string> selectedProgrammingLanguages = new List<string>();
    private string? programmingLanguagesChangedMessage;
    private List<string> programmingLanguageOptions = new() { "C#", "JavaScript", "Python", "Java", "TypeScript", "Go" };

    private void OnProgrammingLanguagesChanged(ICollection<string> newValues)
    {
        // selectedProgrammingLanguages is already updated by @bind-Value.
        // We use newValues here to demonstrate the event payload.
        programmingLanguagesChangedMessage = $"Sprachen geändert zu: {string.Join(", ", newValues ?? new List<string>())} um {DateTime.Now:HH:mm:ss}";
        Console.WriteLine($"Programming languages changed to: {string.Join(", ", newValues ?? new List<string>())}");
    }
}


Component API

Parameter Type Default Description
Items IEnumerable<TItem>? null Gets or sets the collection of items to display in the select dropdown.
ItemValueExpression Expression<Func<TItem, TValue>>? null Gets or sets an expression that specifies which property of TItem should be used as the value for each option. This is the preferred way over ItemValueField for type safety and AOT/trimming compatibility.
ItemTextExpression Expression<Func<TItem, string>>? null Gets or sets an expression that specifies which property of TItem should be used as the display text for each option. This is the preferred way over ItemTextField for type safety and AOT/trimming compatibility.
ItemValueField string? null Gets or sets the name of the property in TItem to use as the value for each option. Use ItemValueExpression for better type safety and AOT/trimming compatibility.
ItemTextField string? null Gets or sets the name of the property in TItem to use as the display text for each option. Use ItemTextExpression for better type safety and AOT/trimming compatibility.
Id string? null Gets or sets the HTML 'id' attribute for the select element. Recommended for accessibility, especially when used with a <label>.
Name string? null Gets or sets the HTML 'name' attribute for the select element.
CssClass string? null Gets or sets additional CSS classes to apply to the select element.
IsDisabled bool false Gets or sets a value indicating whether the select element should be disabled.
AriaLabel string? null Gets or sets the ARIA label for accessibility. Provides a descriptive label for screen readers. Important if no visual label is associated via 'id' or 'for'.
Title string? null Gets or sets the HTML 'title' attribute for the select element. Provides a tooltip on hover and can act as a fallback accessible name.
PlaceholderText string? null Gets or sets the text for a default placeholder option.
PlaceholderValue string string.Empty Gets or sets the value for the default placeholder option.
PlaceholderIsDisabled bool true Gets or sets a value indicating whether the placeholder option should be disabled (not selectable).
NoOptionsText string \"No options available\" Gets or sets the text to display when no options are available (and no placeholder or ChildContent is provided).
ChildContent RenderFragment? null Gets or sets the content to render inside the <select> element, typically used to define custom <option> elements manually.
SkipValidation bool false Gets or sets a value indicating whether to skip the internal configuration validation checks.
Events
OnChanged EventCallback<ICollection<TValue>> - An EventCallback that is invoked when the selected values of the component change. The callback receives the new collection of values as its argument. This complements the @bind-Value parameter.
An unhandled error has occurred. Reload 🗙