GUILD OF GLEKS UIv21.4.4

gog-multiselect

Multiselect

A checkbox-driven multiselect dropdown with optional select-all/clear controls, five sizes, disabled options, placement control, and a body-portaled panel. Once open, Arrow Up/Down move between options, Home/End jump to the first/last, and Escape closes.

Overview

Import the component and drop it into a template.

typescript
import { MultiselectComponent } from '@guildofgleks/ui';

@Component({
  // ...
  imports: [MultiselectComponent],
})

Basic usage — a labeled field bound with [(value)]:

<gog-multiselect label="Features" [options]="features" [(value)]="selectedFeatures" />

Examples

Selected names

The component also exposes a public selectedNames signal — the joined display names of the current selection — reachable straight off a template reference variable, with no summary computed by hand.

Selected: Bug

<gog-multiselect #ms label="Tags" [options]="tags" [(value)]="fullWidthTags" />
<p>Selected: {{ ms.selectedNames() }}</p>

Sizes

Five size steps, from xsm to slg.

@for (sizeOption of sizes; track sizeOption) {
  <gog-multiselect [label]="'Size: ' + sizeOption" [size]="sizeOption" [options]="features" [(value)]="sizeDemoValue" />
}

"Select all" / "Clear" controls

showControls adds a select-all/clear row; controlsPosition puts it above or below the option list — either way it's pinned with position: sticky, so it stays visible while a long list scrolls.

<gog-multiselect
  label="Top (default)"
  [options]="countries"
  [showControls]="true"
  [(value)]="topControlsValue"
/>

<gog-multiselect
  label="Bottom"
  [options]="countries"
  [showControls]="true"
  controlsPosition="bottom"
  [(value)]="bottomControlsValue"
/>

Disabled option & disabled field

A single option can be marked disabled without disabling the whole field, or the trigger itself can be disabled with [disabled]="true". The last field shows an error computed entirely by the page — gog-multiselect just renders whatever errorMessage it's given, for as long as it's non-empty.

Pick at least one option.
<gog-multiselect label="Disabled" [options]="features" [value]="['toast']" [disabled]="true" />

<gog-multiselect label="Permissions (one disabled)" [options]="permissionsWithDisabled" [(value)]="permissions" />

<gog-multiselect
  label="Required tags"
  placeholder="Pick at least one..."
  [options]="permissionsWithDisabled"
  [errorMessage]="requiredError()"
  [(value)]="requiredValue"
/>

Reactive Forms — automatic error timing

Same idea as gog-select: errorDisplay="auto" shows the error once the attached [formControl] has been touched and is invalid, with no manual error computation on this page's side.

<gog-multiselect
  label="Permissions"
  placeholder="Pick at least one..."
  [options]="permissionsWithDisabled"
  [formControl]="permissionsFormControl"
  errorMessage="Pick at least one permission."
  errorDisplay="auto"
/>

Full width

Full width of its container by default (see every field above). [fullWidth]="false" shrinks the trigger to fit its selected summary instead.

<gog-multiselect label="Features" [options]="features" [(value)]="fullWidthFeatures" />
<gog-multiselect label="Tags" [options]="tags" [(value)]="fullWidthTags" [fullWidth]="false" />

Custom chevron & label-less field

Project an <ng-template gogDropdownChevron> to swap the trigger's icon, and <ng-template gogMultiselectClearIcon> to swap the clear control's. ariaLabel names a field that has no visible label at all. Both slots replace inputs deprecated in 21.3.0 and removed in 21.5.0 — chevronTemplate and clearIconTemplate.

<gog-multiselect [options]="sortOptions" [(value)]="sortValue">
  <ng-template gogDropdownChevron>
    <gog-icon name="sort" />
  </ng-template>
  <ng-template gogMultiselectClearIcon>
    <gog-icon name="error" />
  </ng-template>
</gog-multiselect>

<gog-multiselect
  ariaLabel="Tags (no visible label)"
  placeholder="Pick tags"
  [options]="tags"
  [(value)]="ariaOnlyValue"
/>

Append to body & custom panel size

appendToBody portals the panel into document.body — useful inside scrollable or overflow-clipped containers, since the panel escapes the clipping. dropdownWidth and dropdownMaxHeight then take any CSS length to override the trigger-derived size; both apply only with appendToBody. The list below has 20 options, capped to 160px so it scrolls internally.

<gog-multiselect
  label="Country (fixed 240px / 160px panel)"
  [options]="countries"
  [appendToBody]="true"
  dropdownWidth="240px"
  dropdownMaxHeight="160px"
  [(value)]="compactPanelValue"
/>

Your own objects

The same accessors Select takes: optionLabel, optionValue and optionDisabled each accept a property path — dot-paths included — or a function. With [optionValue]="null" the control emits the option objects themselves. Defaults are 'name' / 'id' / 'disabled', so pre-21.3.0 code is unaffected.

value = (empty)

<gog-multiselect
  label="Reviewers"
  optionLabel="profile.fullName"
  optionValue="uuid"
  optionDisabled="suspended"
  [options]="users"
  [(value)]="reviewerIds"
/>

Filtering

filter puts a search box in the panel. The important detail is what it does to "select all": that control takes only the visible options, so it means what it says while a filter is active rather than quietly selecting the whole list. The query resets when the panel closes.

0 selected

<gog-multiselect
  label="Country"
  [filter]="true"
  [showControls]="true"
  filterPlaceholder="Search countries…"
  filterEmptyMessage="No country matches"
  [options]="countries"
  [(value)]="selected"
/>

A long selection collapses to +N

The trigger shows what fits on one line and a count for the rest, with the full list in a tooltip. Select several of these and shrink the window to watch it re-measure: the available space changes when the container resizes, which Angular never renders for, so a ResizeObserver drives it rather than change detection.

0 selected

Custom option rows

A gogDropdownOption template replaces one option row, with the option itself plus selected, disabled and the resolved label in its context. The per-option checkbox stays — it is the control's affordance, not part of the row's content.

<gog-multiselect
  label="Reviewers"
  optionLabel="profile.fullName"
  optionValue="uuid"
  [options]="users"
  [(value)]="reviewerIds"
>
  <ng-template gogDropdownOption let-user let-label="label">
    <strong>{{ label }}</strong>
    <small>{{ user.profile.role }}</small>
  </ng-template>
</gog-multiselect>

API Reference

Inputs

NameTypeDefaultDescription
selectAllLabel21.3.2string | undefined'Select all'Visible text of the panel's select-all button (shown when showControls is on). Also via GOG_CONFIG.labels.selectAll.
clearAllLabel21.3.2string | undefined'Clear'The clear-all button next to it. Also via GOG_CONFIG.labels.clearAll.
value(string | number)[] (model)[]Two-way bindable selected option ids via [(value)]. Also driven by Angular Forms through writeValue/registerOnChange when used with formControlName/[formControl]/ngModel.
labelstring''Field label.
ariaLabelstring''Accessible name for the field when there is no visible label.
placeholderstring'Select...'Text shown while no option is selected.
optionsTOption[][]The list of choices — your own objects. GogDropdownOption ({ id, name, disabled? }) is just the shape the default accessors expect, not a requirement.
optionLabelstring | ((o: TOption) => string)'name'How an option turns into its visible text: a property path (dot-paths included) or a function.
optionValuestring | ((o: TOption) => unknown) | null'id'How an option turns into an emitted value. Set it to null and the control emits the option OBJECTS themselves.
optionDisabledstring | ((o: TOption) => boolean)'disabled'Which options cannot be picked.
clearablebooleanGOG_CONFIG.control.clearable ?? trueAdds a clear button in the outermost trailing position. Deliberately the one control that defaults to true: gog-multiselect shipped a clear button before this input existed, and defaulting it to false would have silently removed it.
clearAriaLabelstring'Clear selection'Accessible name for that clear button.
filterbooleanGOG_CONFIG.dropdown.filter ?? falsePuts a search box in the panel, matching case-insensitively on the resolved optionLabel. "Select all" then takes only the VISIBLE options, so it means what it says while a filter is active.
filterPosition'top' | 'bottom'GOG_CONFIG.dropdown.filterPosition ?? 'top'Which end of the panel the search box sticks to — the same vocabulary as controlsPosition, rather than a second one for the same idea.
filterPlaceholder / filterEmptyMessagestring'Search...' / 'No matches'Wording for the search box and for the empty result.
filterMatch((option: TOption, query: string) => boolean) | nullnullReplaces the default case-insensitive substring match with your own predicate.
floatLabel'none' | 'in' | 'on' | 'over'GOG_CONFIG.floatLabel.variant ?? 'none'Rests the label inside the field like a placeholder and floats it up once the selection is non-empty or the field has focus.
floatLabelShowPlaceholderbooleanGOG_CONFIG.floatLabel.showPlaceholder ?? falseReveals the placeholder once the label has floated out of the way.
minWidthstring | nullnull (--gog-multiselect-min-width, 120px)Floor for an auto-width trigger, any CSS length — so a short selection cannot collapse the field to its own chrome.
showControlsbooleanfalseShows a "select all" / "clear" row above (or below) the option list.
controlsPosition'top' | 'bottom''top'Where the select-all/clear row sits relative to the option list. Sticky either way, so it stays visible while a long list scrolls.
clearIconTemplateTemplateRef<unknown> | nullnullDeprecated since 21.3.0, removed in 21.5.0 — project an <ng-template gogMultiselectClearIcon> instead. Still works, and the projected slot wins when both are present.
errorMessagestring''Error text to display. Visibility is governed by errorDisplay.
errorDisplay'auto' | 'manual'GOG_CONFIG.control.errorDisplay ?? 'manual''manual': shown for as long as errorMessage is non-empty — you decide the timing. 'auto': shown once the attached FormControl is touched and invalid; falls back to manual without one.
size'xsm' | 'sm' | 'md' | 'lg' | 'slg'GOG_CONFIG.control.size ?? 'md'Field height, padding, and font size.
disabledbooleanfalseDisables the trigger and closes the panel if it is open.
fullWidthbooleantrueFills its container by default. Set false to shrink to fit the selected summary instead.
dropdownDirection'auto' | 'up' | 'down'GOG_CONFIG.dropdown.direction ?? 'auto'Which side the panel opens on. 'auto' flips to whichever side has room in the viewport.
dropdownZIndexnumber | nullnullExplicit stacking order for the panel. Left unset it falls back to the --gog-dropdown-z token.
dropdownWidthstring | nullnullFixed panel width, any CSS length. Applies only with appendToBody. Left unset, the panel sizes to its own content with the trigger width as a floor, capped by --gog-{select,multiselect}-panel-max-width — so picking a short option no longer cuts the longer ones off the list.
dropdownMaxHeightstring | nullnullFixed panel max-height, any CSS length. Applies only with appendToBody.
appendToBodybooleanGOG_CONFIG.dropdown.appendToBody ?? falsePortals the panel into document.body instead of rendering it inline — escapes an ancestor's scroll/overflow clipping.
chevronTemplateTemplateRef<unknown> | nullnullDeprecated since 21.3.0, removed in 21.5.0 — project an <ng-template gogDropdownChevron> instead. Still works, and the projected slot wins when both are present.

Content slots

DirectiveContextDescription
gogDropdownOption$implicit, selected, disabled, labelReplaces one option row. The per-option checkbox stays.
gogDropdownChevron Replaces the trigger's arrow. Wins over the deprecated chevronTemplate input.
gogMultiselectClearIcon Replaces the clear control's icon. Wins over the deprecated clearIconTemplate input.

Styling Tokens

Every CSS custom property the multiselect paints with. Override any of them — on a single instance, a subtree, or a theme — to restyle it. See the Theming guide for the full token-layering model, or the Theme Generator to tweak these live.

TokenDescription
--gog-multiselect-* (was --gog-ms-*)Renamed in 21.3.0. Both spellings work for the whole deprecation window — the --gog-ms-* name stays the declared one and the new name derives from it, so an existing override of either still reaches the component. --gog-ms-* is removed in 21.5.0.
--gog-multiselect-label-colorField label color.
--gog-multiselect-field-bg / -field-borderField surface and border.
--gog-multiselect-radius / -min-widthField corner radius, and the floor an auto-width trigger cannot collapse past.
--gog-multiselect-focus-border / -focus-ringFocus state.
--gog-multiselect-panel-bg / -panel-border / -panel-shadow / -panel-max-widthDropdown panel surface, and the cap on a panel that sizes to its own content rather than to the trigger.
--gog-multiselect-option-hover-bg / -option-colorOption row, default and hover.
--gog-multiselect-checkbox-bg / -checkbox-checked-bgPer-option selection checkbox.
--gog-multiselect-float-label-reserve / -in-top / -on-bg / -over-gap / -over-reserveFloat-label geometry, derived from the shared --gog-field-float-label-* scale.