nextgenSOFTWARE
SOFTWARE NEXT GENERATION

NgMultiSelect — Multiple selection

Live component previewTry the controls · demonstration data
Open preview ↗

Manual · Getting started · Code examples · Models

When to use it

Select several NgOption keys and remove selected items.

Appearance and live example

Try the Blazor example · Flutter example code

The live Blazor preview is embedded above. Use Reset to restore its initial state. Flutter implementation code is provided below; run the Flutter gallery from the installed developer workspace. Dialogs and drawers start closed; use their Open button. Tooltips appear on hover or keyboard focus.

Behavior and practical rules

Controlled selection

Value/value is an array/list of option IDs. Items require unique nonempty IDs and nonempty labels. The parent stores emitted copies and passes them back; never mutate an emitted list or source collection in place. Flutter validation reads the current controlled value, including parent-driven changes. In Blazor use model data annotations for required/minimum selection; Flutter exposes required. MaxSelected/maxSelected must be positive if supplied and limits user additions, not externally supplied values.

Disabled options are locked: they cannot be added, removed through a chip, or cleared by bulk actions. An already-selected disabled ID counts toward the limit. Unknown selected IDs are shown as their raw ID and can be removed; the application decides how to reconcile removed records. ReadOnly/readOnly freezes selection while still allowing the list to be filtered. Disabled/enabled=false also disables filtering.

Search, templates and bulk actions

FilterMode/filterMode accepts contains, startsWith or endsWith; MinLength/minLength defaults to 0. Placeholder and EmptyText/emptyText customize search and no-match guidance. Filtering never removes selections. ItemTemplate/itemBuilder and TagTemplate/tagBuilder decorate list items and selected chips while retaining accessible model labels. Use short noninteractive content.

ShowActions/showActions adds Select filtered and Clear selection. Select filtered takes every enabled match plus the existing selection, preserving order and removing duplicates. If the combined selection exceeds MaxSelected, the entire action is rejected with a message: no partial selection. Clear selection removes every unlocked selection, including those hidden by the filter, and retains disabled selections. These bulk actions are original Nextgen behavior, not a claim of third-party API compatibility.

Remaining scope

This implementation is an inline checklist with selected chips, not a popup MultiSelect. Remote data, virtualization, grouping, custom values, summary tags, popup timing/close behavior, localization of every action/status, complete keyboard-focus management and screen-reader/native certification remain open. Case folding follows each platform, not culture-specific collation.

Blazor example

Razor / Blazor
<NgCheckBox Label="Read only" @bind-Value="locked"/>
<NgMultiSelect Label="Services" Items="services" @bind-Value="selected" MaxSelected="3" ShowActions ReadOnly="locked" EmptyText="No matching services">
<ItemTemplate Context="service"><strong>@service.Label</strong><small> · service</small></ItemTemplate>
<TagTemplate Context="service"><span>✓ @service.Label</span></TagTemplate>
</NgMultiSelect>
<p>Selected IDs: @string.Join(", ",selected)</p>
@code {private bool locked;private string[] selected=["locked"];private readonly NgOption[] services=[new("a","Alpha"),new("b","Beta"),new("g","Gamma"),new("locked","Mandatory",true)];}

Flutter example

Dart / Flutter
bool locked=false;List<String> selected=['locked'];
final services=const [NgOption('a','Alpha'),NgOption('b','Beta'),NgOption('g','Gamma'),NgOption('locked','Mandatory',disabled:true)];
Widget build(BuildContext context)=>Column(crossAxisAlignment:CrossAxisAlignment.stretch,children:[
NgCheckBox(label:'Read only',value:locked,onChanged:(v)=>setState(()=>locked=v)),
NgMultiSelect(label:'Services',value:selected,items:services,maxSelected:3,showActions:true,readOnly:locked,emptyText:'No matching services',onChanged:(v)=>setState(()=>selected=v),itemBuilder:(_,o)=>Text('${o.label} · service'),tagBuilder:(_,o)=>Text('✓ ${o.label}')),
Text('Selected IDs: ${selected.join(', ')}')]);

Blazor parameters and events

Names are case-sensitive. ? permits null. EditorRequired parameters are marked required; other values use the initializer or CLR default.

Parameter Type Default / requirement Purpose
Label string "Options" Visible label or accessible name.
Items IReadOnlyList<NgOption> [] Input collection. Replace it when changing records or configuration.
MaxSelected int? not set See the behavior rules and model contract above.
Disabled bool false Disable user interaction.
ReadOnly bool false Prevent changes while retaining supported viewing actions.
ShowActions bool false See the behavior rules and model contract above.
FilterMode string "contains" See the behavior rules and model contract above.
MinLength int 0 See the behavior rules and model contract above.
Placeholder string "Filter options…" Hint displayed for empty input.
EmptyText string "No matches" See the behavior rules and model contract above.
ItemTemplate RenderFragment<NgOption>? not set See the behavior rules and model contract above.
TagTemplate RenderFragment<NgOption>? not set See the behavior rules and model contract above.
Value string[] model value Current field value.
ValueChanged EventCallback<string[]> not set Value change callback.
ValueExpression Expression<Func<string[]>> required Supplied by @bind-Value; pass explicitly with manual binding.
AdditionalAttributes HTML attribute dictionary null Extra HTML/ARIA attributes.

Flutter parameters and events

required marks a mandatory argument even if its type is nullable. key is the standard Flutter Key. The exact constructor below specifies defaults.

Argument Type Purpose
label String Visible label or accessible name.
value List<String> Current value; see state ownership and model rules above.
items List<NgOption> Input collection. Replace it when changing records or configuration.
onChanged ValueChanged<List<String>>? Store the new value using setState or your state manager.
maxSelected int? See the behavior rules and model contract above.
enabled bool See the behavior rules and model contract above.
required bool Require a nonempty/selected value during form validation.
readOnly bool Prevent changes while retaining supported viewing actions.
showActions bool See the behavior rules and model contract above.
filterMode String See the behavior rules and model contract above.
placeholder String Hint displayed for empty input.
emptyText String See the behavior rules and model contract above.
minLength int See the behavior rules and model contract above.
itemBuilder Widget Function(BuildContext, NgOption)? See the behavior rules and model contract above.
tagBuilder Widget Function(BuildContext, NgOption)? See the behavior rules and model contract above.
Dart / Flutter
const NgMultiSelect({
    super.key,
    this.label = 'Options',
    required this.value,
    required this.items,
    this.onChanged,
    this.maxSelected,
    this.enabled = true,
    this.required = false,
    this.readOnly = false,
    this.showActions = false,
    this.filterMode = 'contains',
    this.minLength = 0,
    this.placeholder = 'Filter options…',
    this.emptyText = 'No matches',
    this.itemBuilder,
    this.tagBuilder,
  });

Integration and troubleshooting

If the UI does not update, confirm that the parent stores callback values and passes them back. Blazor event handlers need interactive rendering; Flutter requires setState or a state manager. Submit form data only after successful validation.

Check accessible labels, empty/invalid states, disabled actions and narrow layouts. Keyboard support is not full screen-reader certification. Current visual verification targets Web in visible Chrome on Linux/iMac; native mobile platforms are a separate phase.

Source and complete examples

API declarations are generated from current source. Curated examples and behavior live in scripts/manual_content.py; regenerate with python3 scripts/generate_manual.py.