Dropdown (Multi)

A searchable drop down with one selection

Purpose of Component

RfDropDownMulti creates a Bulma dropdown that supports searching and manages the selection of an multiple items.

When not to Use

Don't use RfDropDownMulti when:

  • When using static rendering mode. Instead use InputSelect.

Basic Usage

The RfDropDownMulti works primarily with two parameters: SelectedItems and Items.

Binding the selection

The selection itself has two parameters: SelectedItems is the list of chosen items and SelectedItemsChanged is raised after every click that changes the selection. Bind them together with @bind-SelectedItems, or bind only SelectedItemsChanged and let the drop down keep the list: the component starts an empty list on the first item click, adds the clicked item, and emits SelectedItemsChanged, so the callback fires from the very first click even with no SelectedItems parameter set. Later clicks add and remove items from that same list and every emit carries it, so the callback alone is a complete selection contract. The list is started before the MaxSelectableItems cap is consulted, so the cap and MaxSelectionKeepRule behave identically whether or not you supply SelectedItems.

Example: selection via callback only
<RfDropDownMulti TItem="string"
                   Items="@Items"
                   SelectedItemsChanged="@OnSelectedChanged" />
private void OnSelectedChanged(List<string> selected)
{
    // The first click on an item delivers a one item list; no SelectedItems parameter is needed.
}

How are Items Render?

The dropdown doesn't make any assumptions on how to render the items or selected items it is given. Instead the drop down supports three RenderFragement to customize the way an item appears within the drop down. All three RenderFragement have a context of TItem and are used to setup two sections. The two sections of the drop down that can be custimized are RowItemTemplate and SelectedItemTemplate.

Row Item Template

The RowItemTempalte directly sets each row or option of the drop down. Each row is wrapped in the following code:

<a class="dropdown-item">@RowItemTemplate(item)</a>
If RowItemTemplate isn't set the drop down will use ChildContent.

Selected Items Template

When the drop down has a selection it calls the SelectedItemTemplate to render the TItem within the input portion of the drop down. Each selected item is wrapped in the following code:

<span class="tag @CssSelectedClass">@SelectedItemTemplate(selectedItem)</span>
If SelectedItemTemplate isn't set the drop down will use ChildContent.

Loading Items

When the items come from a service instead of a static list, set OnLoad. The drop down calls it with the current filter text and expects your handler to assign Items. Clicking the trigger runs the open sequence: the loading state is applied (Bulma shows the spinner on the trigger icon), OnLoad is awaited, the loading state is cleared, and the panel opens only after your handler has returned.

An attempt to open while a load is in flight is ignored: no second OnLoad starts and the component keeps the state of the load that is running. Typing in the filter re-invokes OnLoad with the new filter text while the panel stays open; set FilterRateLimit to hold keystrokes until the rate elapses and fire only the latest filter.

Filter rate limiting

While the panel stays open, every keystroke in the filter box queues a filter change instead of calling OnLoad directly. When FilterRateLimit is greater than zero (the default is 250 ms) the drop down waits until the newest keystroke has sat quietly for that many milliseconds, then makes one OnLoad call with the latest filter text; keystrokes typed during the wait are superseded and dropped. Any new keystroke restarts the wait, so the load fires on the trailing edge of the typing. FilterRateLimit = 0 turns the wait off and fires on every keystroke.

The wait is measured on a monotonic clock (stopwatch timestamps) and each sleep is only the time remaining in the quiet window, so the window length never depends on the wall clock: NTP corrections or manual clock changes cannot stretch the debounce or break it.

Loader exceptions

The loading state is released in a finally, so a handler that throws cannot latch the drop down into a permanent loading state. The failed attempt leaves the drop down closed, the exception still propagates to your app's event handling (Blazor's unhandled exception path unless you catch it), and the next click calls OnLoad again. Before this fix a throwing handler left the loading state set forever and the drop down could never be opened again.

NOTE: If a load can fail and you still want the panel to open, catch the exception inside your OnLoad handler: when the handler returns normally the panel opens with whatever you left in Items; when it throws, the attempt ends closed.
Example: loading items on open
<RfDropDownMulti TItem="string"
                   Items="@Items"
                   @bind-SelectedItems="@Selected"
                   OnLoad="@LoadNames" />
private async Task LoadNames(string filter)
{
    Items = await NameService.SearchAsync(filter);
}

Styling

The drop down passes four class parameters through to the elements it renders, so you can style it without fighting the component:

  • CssClass is added to the root div.dropdown element, alongside the state classes the component manages itself (is-active while open, is-right and is-up from Position).
  • CssFilterClass is added to the filter textbox alongside the Bulma input class. It has no effect while ShowFilter hides the filter.
  • CssRowItemClass is added to every option row inside the drop down panel.
  • CssSelectedClass is added to each selected item tag in the trigger.

Unset parameters add nothing: the rendered markup is identical to a drop down that never had them.

Example:
<RfDropDownMulti TItem="string"
                       Items="@Items"
                       @bind-SelectedItems="@Selected"
                       CssClass="wide-dropdown"
                       CssFilterClass="is-rounded" />

With the panel open this renders the root as class="dropdown is-active wide-dropdown", each selection as class="tag", and the filter textbox as class="input is-rounded".

How to Setup

Setting up RForge is simple. Follow the below steps to add RForge Blazor components to your project.

  1. Install the Nuget package
  2. Import the namespaces (optional)
  3. Register RForge services
  4. Include Bulma CSS

Install the Nuget Package

RForge Blazor components are installed via Nuget. There are two packages. One for the blazor app and one for the library if any.

Blazor Project Installation

Command Line
dotnet add package RForge.Blazor
Package Manager Console
Install-Package RForge.Blazor

Library Project Installation

This is optional but may be useful if you want to use common enums across the backend and frontend.

Command Line
dotnet add package RForge.Abstractions
Package Manager Console
Install-Package RForge.Abstractions

Import the namespaces

To simplify namespacing while using RForge, the package is designed with most all components sitting within the root RForge namespace.

You may want to include the following namespaces in your _import.razor for ease of use.

@using RForgeBlazor
@using RForgeBlazor.Services
@using RForge.Abstractions
Namespace Purpose
RForgeBlaozr Houses all of the blazor components.
RForgeBlaozr.Services Houses interfaces you may need to communicate with certain components.
RForgeBlaozr.Abstractions Common enums used through out the components.

Register RForge Services

In order for some components to work they must first have their services registered.

Open up Program.cs and add the following line before the var app = builder.Build();.

Heads Up!
If your project is using the multi project solution where one project is server and the other is client. You will need to register the services in both projects.

using RForgeBlazor;
//...
builder.Services.AddRfForgeBlazorServices();
//...
var app = builder.Build();
    

Include Bulma CSS

RForge makes use of Bulma CSS to stylize the components. Add / install Bulma and add the CSS file to the head. Link to download: Bulma releases.

An unhandled error has occurred. Reload 🗙