or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

accessibility.mdaccordion.mdcollections-data.mddialogs.mddrag-drop.mdindex.mdlistbox.mdmenus.mdobservers.mdoverlays.mdplatform-utilities.mdportals.mdscrolling.mdtesting.mdtext-fields.md
tile.json

listbox.mddocs/

Listbox

The Angular CDK Listbox module provides accessible listbox components with single and multiple selection support, keyboard navigation, and form control integration. It implements the ARIA listbox pattern with full accessibility compliance.

Capabilities

Listbox Component

The main listbox component that manages selection state and accessibility.

/**
 * Directive for creating accessible listboxes with single or multiple selection
 */
class CdkListbox<T = unknown> implements ControlValueAccessor, AfterContentInit, OnDestroy {
  /**
   * The id of the listbox
   */
  id: string;

  /**
   * Whether multiple options can be selected
   */
  multiple: boolean;

  /**
   * Whether the listbox is disabled
   */
  disabled: boolean;

  /**
   * Function used to compare option values
   */
  compareWith: (o1: T, o2: T) => boolean;

  /**
   * Whether to use active descendant for focus management
   */
  useActiveDescendant: boolean;

  /**
   * The orientation of the listbox (vertical or horizontal)
   */
  orientation: 'vertical' | 'horizontal';

  /**
   * Whether the listbox should wrap focus when reaching the end
   */
  navigationWrapDisabled: boolean;

  /**
   * Whether to skip disabled options during keyboard navigation
   */
  navigateDisabledOptions: boolean;

  /**
   * The currently selected value(s)
   */
  value: T | T[] | null;

  /**
   * Event emitted when the selection changes
   */
  readonly selectionChange: EventEmitter<ListboxSelectionChangeEvent<T>>;

  /**
   * Event emitted when an option is activated via keyboard navigation
   */
  readonly optionActivated: EventEmitter<ListboxOptionActivatedEvent<T>>;

  /**
   * The listbox options
   */
  readonly options: QueryList<CdkOption<T>>;

  /**
   * Focus the listbox
   */
  focus(): void;

  /**
   * Toggle selection of the given value
   * @param value - The value to toggle
   */
  toggle(value: T): void;

  /**
   * Toggle selection of all options
   */
  toggleAll(): void;

  /**
   * Select the given value(s)
   * @param values - Value(s) to select
   */
  select(...values: T[]): void;

  /**
   * Deselect the given value(s)
   * @param values - Value(s) to deselect
   */
  deselect(...values: T[]): void;

  /**
   * Set the selected values
   * @param values - The values to select
   */
  setSelectedValues(values: T[]): void;

  /**
   * Set all options to the given selected state
   * @param isSelected - Whether options should be selected
   */
  setAllSelected(isSelected: boolean): void;

  /**
   * Check if a value is selected
   * @param value - The value to check
   * @returns Whether the value is selected
   */
  isSelected(value: T): boolean;

  /**
   * Check if all options are selected
   * @returns Whether all options are selected
   */
  isAllSelected(): boolean;

  /**
   * Get all selected values
   * @returns Array of selected values
   */
  getSelectedValues(): T[];

  // ControlValueAccessor implementation
  writeValue(value: T | T[] | null): void;
  registerOnChange(fn: (value: T | T[] | null) => void): void;
  registerOnTouched(fn: () => void): void;
  setDisabledState(isDisabled: boolean): void;
}

Usage Example:

import { Component } from '@angular/core';
import { CdkListboxModule } from '@angular/cdk/listbox';
import { FormControl } from '@angular/forms';

@Component({
  template: `
    <!-- Single selection listbox -->
    <div cdkListbox [formControl]="singleSelection">
      <div cdkOption="red">Red</div>
      <div cdkOption="green">Green</div>
      <div cdkOption="blue">Blue</div>
    </div>

    <!-- Multiple selection listbox -->
    <div cdkListbox multiple [formControl]="multipleSelection">
      <div cdkOption="apple">Apple</div>
      <div cdkOption="banana">Banana</div>
      <div cdkOption="cherry">Cherry</div>
      <div cdkOption="date">Date</div>
    </div>

    <!-- With object values and compareWith -->
    <div cdkListbox [compareWith]="compareUsers" [formControl]="userSelection">
      <div *ngFor="let user of users" [cdkOption]="user">
        {{ user.name }}
      </div>
    </div>
  `,
  standalone: true,
  imports: [CdkListboxModule]
})
export class ListboxExample {
  singleSelection = new FormControl('red');
  multipleSelection = new FormControl(['apple', 'cherry']);
  userSelection = new FormControl(null);

  users = [
    { id: 1, name: 'John Doe' },
    { id: 2, name: 'Jane Smith' },
    { id: 3, name: 'Bob Johnson' }
  ];

  compareUsers(u1: any, u2: any): boolean {
    return u1 && u2 ? u1.id === u2.id : u1 === u2;
  }
}

Listbox Option

Individual option component within a listbox.

/**
 * Directive for listbox options
 */
class CdkOption<T = unknown> implements OnDestroy {
  /**
   * The id of the option
   */
  id: string;

  /**
   * Whether the option is disabled
   */
  disabled: boolean;

  /**
   * The value of the option
   */
  value: T;

  /**
   * Whether the option is selected
   */
  readonly selected: boolean;

  /**
   * Whether the option is active (focused via keyboard navigation)
   */
  readonly active: boolean;

  /**
   * Event emitted when the option's selection state changes
   */
  readonly selectionChange: EventEmitter<CdkOptionSelectionChange<T>>;

  /**
   * Reference to the parent listbox
   */
  readonly listbox: CdkListbox<T>;

  /**
   * Select this option
   */
  select(): void;

  /**
   * Deselect this option
   */
  deselect(): void;

  /**
   * Toggle the selection state of this option
   */
  toggle(): void;

  /**
   * Focus this option
   */
  focus(): void;

  /**
   * Get the label text for this option
   */
  getLabel(): string;
}

Selection Model

Custom selection model for managing listbox selection state.

/**
 * Custom selection model for listbox components
 */
class ListboxSelectionModel<T> {
  /**
   * Whether multiple selections are allowed
   */
  readonly multiple: boolean;

  /**
   * Currently selected values
   */
  readonly selected: T[];

  /**
   * Event emitted when selection changes
   */
  readonly changed: Observable<ListboxSelectionChange<T>>;

  /**
   * Whether a value is selected
   * @param value - The value to check
   * @returns Whether the value is selected
   */
  isSelected(value: T): boolean;

  /**
   * Check if multiple values are selected
   * @param values - Values to check
   * @returns Whether all values are selected
   */
  isMultipleSelected(values: T[]): boolean;

  /**
   * Select value(s)  
   * @param values - Values to select
   */
  select(...values: T[]): void;

  /**
   * Deselect value(s)
   * @param values - Values to deselect
   */
  deselect(...values: T[]): void;

  /**
   * Toggle selection of value(s)
   * @param values - Values to toggle
   */
  toggle(...values: T[]): void;

  /**
   * Clear all selections
   */
  clear(): void;

  /**
   * Determine which values to select and deselect
   * @param values - New values to be selected
   * @returns Object with values to add and remove
   */
  determineSelectionResult(values: T[]): {
    added: T[];
    removed: T[];
  };
}

Advanced Listbox Example:

import { Component } from '@angular/core';
import { CdkListboxModule } from '@angular/cdk/listbox';

@Component({
  template: `
    <div 
      cdkListbox 
      multiple
      [disabled]="isDisabled"
      orientation="vertical"
      [navigationWrapDisabled]="false"
      [navigateDisabledOptions]="false"
      (selectionChange)="onSelectionChange($event)"
      (optionActivated)="onOptionActivated($event)">
      
      <div 
        *ngFor="let item of items; trackBy: trackByItem" 
        [cdkOption]="item"
        [disabled]="item.disabled"
        class="listbox-option"
        [class.selected]="isSelected(item)"
        [class.disabled]="item.disabled">
        
        <span class="option-icon">{{ item.icon }}</span>
        <span class="option-label">{{ item.label }}</span>
        <span class="option-description">{{ item.description }}</span>
      </div>
    </div>

    <div class="selection-info">
      <p>Selected items: {{ selectedItems.length }}</p>
      <ul>
        <li *ngFor="let item of selectedItems">{{ item.label }}</li>
      </ul>
    </div>
  `,
  styles: [`
    .listbox-option {
      padding: 8px 12px;
      border: 1px solid #ccc;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    
    .listbox-option.selected {
      background-color: #e3f2fd;
      border-color: #2196f3;
    }
    
    .listbox-option.disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
    
    .option-icon {
      font-size: 16px;
    }
    
    .option-description {
      color: #666;
      font-size: 12px;
    }
  `],
  standalone: true,
  imports: [CdkListboxModule]
})
export class AdvancedListboxExample {
  isDisabled = false;
  selectedItems: any[] = [];

  items = [
    { id: 1, label: 'Documents', icon: '📄', description: 'Text documents', disabled: false },
    { id: 2, label: 'Images', icon: '🖼️', description: 'Image files', disabled: false },
    { id: 3, label: 'Videos', icon: '🎥', description: 'Video files', disabled: true },
    { id: 4, label: 'Audio', icon: '🎵', description: 'Audio files', disabled: false },
    { id: 5, label: 'Archives', icon: '📦', description: 'Compressed files', disabled: false }
  ];

  onSelectionChange(event: any) {
    this.selectedItems = event.value || [];
    console.log('Selection changed:', this.selectedItems);
  }

  onOptionActivated(event: any) {
    console.log('Option activated:', event.option.value);
  }

  isSelected(item: any): boolean {
    return this.selectedItems.some(selected => selected.id === item.id);
  }

  trackByItem(index: number, item: any): any {
    return item.id;
  }
}

Events and Types

/**
 * Event emitted when listbox selection changes
 */
interface ListboxSelectionChangeEvent<T> {
  /**
   * The listbox that triggered the event
   */
  source: CdkListbox<T>;

  /**
   * The new selected value(s)
   */
  value: T | T[] | null;
}

/**
 * Event emitted when an option is activated via keyboard navigation
 */
interface ListboxOptionActivatedEvent<T> {
  /**
   * The listbox that contains the activated option
   */
  source: CdkListbox<T>;

  /**
   * The activated option
   */
  option: CdkOption<T> | null;

  /**
   * The previously activated option
   */
  previousOption: CdkOption<T> | null;
}

/**
 * Event emitted when an option's selection state changes
 */
interface CdkOptionSelectionChange<T> {
  /**
   * The option that triggered the event
   */
  source: CdkOption<T>;

  /**
   * Whether the option is selected
   */
  selected: boolean;
}

/**
 * Change event for listbox selection model
 */
interface ListboxSelectionChange<T> {
  /**
   * Values that were added to the selection
   */
  added: T[];

  /**
   * Values that were removed from the selection
   */
  removed: T[];

  /**
   * The selection model that triggered the change
   */
  source: ListboxSelectionModel<T>;
}

CDK Listbox Module

/**
 * Angular module that includes all CDK listbox directives
 */
class CdkListboxModule {}