primeng
Version:
[](https://badge.fury.io/js/primeng) [](https://www.npmjs.com/package/primeng) [
{"version":3,"file":"primeng-multiselect.mjs","sources":["../../src/app/components/multiselect/multiselect.ts","../../src/app/components/multiselect/primeng-multiselect.ts"],"sourcesContent":["import { AnimationEvent } from '@angular/animations';\nimport { CommonModule } from '@angular/common';\nimport {\n AfterContentInit,\n AfterViewChecked,\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n computed,\n ContentChild,\n ContentChildren,\n effect,\n ElementRef,\n EventEmitter,\n forwardRef,\n Input,\n NgModule,\n NgZone,\n OnInit,\n Output,\n QueryList,\n Renderer2,\n signal,\n SimpleChanges,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { FilterService, Footer, Header, OverlayOptions, OverlayService, PrimeNGConfig, PrimeTemplate, SharedModule, TranslationKeys } from 'primeng/api';\nimport { DomHandler } from 'primeng/dom';\nimport { Overlay, OverlayModule } from 'primeng/overlay';\nimport { RippleModule } from 'primeng/ripple';\nimport { Scroller, ScrollerModule } from 'primeng/scroller';\nimport { ScrollerOptions } from 'primeng/api';\nimport { TooltipModule } from 'primeng/tooltip';\nimport { ObjectUtils, UniqueComponentId } from 'primeng/utils';\nimport { CheckIcon } from 'primeng/icons/check';\nimport { SearchIcon } from 'primeng/icons/search';\nimport { TimesCircleIcon } from 'primeng/icons/timescircle';\nimport { TimesIcon } from 'primeng/icons/times';\nimport { ChevronDownIcon } from 'primeng/icons/chevrondown';\nimport { Nullable } from 'primeng/ts-helpers';\nimport { MultiSelectRemoveEvent, MultiSelectFilterOptions, MultiSelectFilterEvent, MultiSelectBlurEvent, MultiSelectChangeEvent, MultiSelectFocusEvent, MultiSelectLazyLoadEvent, MultiSelectSelectAllChangeEvent } from './multiselect.interface';\n\nexport const MULTISELECT_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => MultiSelect),\n multi: true\n};\n\n@Component({\n selector: 'p-multiSelectItem',\n template: `\n <li\n pRipple\n [ngStyle]=\"{ height: itemSize + 'px' }\"\n class=\"p-multiselect-item\"\n [ngClass]=\"{ 'p-multiselect-item': true, 'p-highlight': selected, 'p-disabled': disabled, 'p-focus': focused }\"\n [id]=\"id\"\n [attr.aria-label]=\"label\"\n [attr.aria-setsize]=\"ariaSetSize\"\n [attr.aria-posinset]=\"ariaPosInset\"\n [attr.aria-selected]=\"selected\"\n [attr.data-p-focused]=\"focused\"\n [attr.data-p-highlight]=\"selected\"\n [attr.data-p-disabled]=\"disabled\"\n (click)=\"onOptionClick($event)\"\n (mouseenter)=\"onOptionMouseEnter($event)\"\n >\n <div class=\"p-checkbox p-component\">\n <div class=\"p-checkbox-box\" [ngClass]=\"{ 'p-highlight': selected }\" [attr.aria-checked]=\"selected\">\n <ng-container *ngIf=\"selected\">\n <CheckIcon *ngIf=\"!checkIconTemplate\" [styleClass]=\"'p-checkbox-icon'\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"checkIconTemplate\" class=\"p-checkbox-icon\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"checkIconTemplate\"></ng-template>\n </span>\n </ng-container>\n </div>\n </div>\n <span *ngIf=\"!template\">{{ label ?? 'empty' }}</span>\n <ng-container *ngTemplateOutlet=\"template; context: { $implicit: option }\"></ng-container>\n </li>\n `,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'p-element'\n }\n})\nexport class MultiSelectItem {\n @Input() id: string | undefined;\n\n @Input() option: any;\n\n @Input() selected: boolean | undefined;\n\n @Input() label: string | undefined;\n\n @Input() disabled: boolean | undefined;\n\n @Input() itemSize: number | undefined;\n\n @Input() focused: boolean | undefined;\n\n @Input() ariaPosInset: string | undefined;\n\n @Input() ariaSetSize: string | undefined;\n\n @Input() template: TemplateRef<any> | undefined;\n\n @Input() checkIconTemplate: TemplateRef<any> | undefined;\n\n @Output() onClick: EventEmitter<any> = new EventEmitter();\n\n @Output() onMouseEnter: EventEmitter<any> = new EventEmitter();\n\n onOptionClick(event: Event) {\n this.onClick.emit({\n originalEvent: event,\n option: this.option,\n selected: this.selected\n });\n event.stopPropagation();\n }\n\n onOptionMouseEnter(event: Event) {\n this.onMouseEnter.emit({\n originalEvent: event,\n option: this.option,\n selected: this.selected\n });\n }\n}\n/**\n * MultiSelect is used to select multiple items from a collection.\n * @group Components\n */\n@Component({\n selector: 'p-multiSelect',\n template: `\n <div #container [attr.id]=\"id\" [ngClass]=\"containerClass\" [ngStyle]=\"style\" [class]=\"styleClass\" (click)=\"onContainerClick($event)\">\n <div class=\"p-hidden-accessible\" [attr.data-p-hidden-accessible]=\"true\">\n <input\n #focusInput\n [pTooltip]=\"tooltip\"\n [tooltipPosition]=\"tooltipPosition\"\n [positionStyle]=\"tooltipPositionStyle\"\n [tooltipStyleClass]=\"tooltipStyleClass\"\n [attr.aria-disabled]=\"disabled\"\n [attr.id]=\"inputId\"\n role=\"combobox\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledBy\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"overlayVisible\"\n [attr.aria-controls]=\"id + '_list'\"\n [attr.tabindex]=\"!disabled ? tabindex : -1\"\n [attr.aria-activedescendant]=\"focused ? focusedOptionId : undefined\"\n (focus)=\"onInputFocus($event)\"\n (blur)=\"onInputBlur($event)\"\n (keydown)=\"onKeyDown($event)\"\n />\n </div>\n <div class=\"p-multiselect-label-container\" [pTooltip]=\"tooltip\" [tooltipPosition]=\"tooltipPosition\" [positionStyle]=\"tooltipPositionStyle\" [tooltipStyleClass]=\"tooltipStyleClass\">\n <div [ngClass]=\"labelClass\">\n <ng-container *ngIf=\"!selectedItemsTemplate\">\n <ng-container *ngIf=\"display === 'comma'\">{{ label() || 'empty' }}</ng-container>\n <ng-container *ngIf=\"display === 'chip'\">\n <div #token *ngFor=\"let item of chipSelectedItems(); let i = index\" class=\"p-multiselect-token\">\n <span class=\"p-multiselect-token-label\">{{ getLabelByValue(item) }}</span>\n <ng-container *ngIf=\"!disabled\">\n <TimesCircleIcon *ngIf=\"!removeTokenIconTemplate\" [styleClass]=\"'p-multiselect-token-icon'\" (click)=\"removeOption(item, event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"removeTokenIconTemplate\" class=\"p-multiselect-token-icon\" (click)=\"removeOption(item, event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\">\n <ng-container *ngTemplateOutlet=\"removeTokenIconTemplate\"></ng-container>\n </span>\n </ng-container>\n </div>\n <ng-container *ngIf=\"!modelValue() || modelValue().length === 0\">{{ placeholder || defaultLabel || 'empty' }}</ng-container>\n </ng-container>\n </ng-container>\n <ng-container *ngTemplateOutlet=\"selectedItemsTemplate; context: { $implicit: selectedOptions, removeChip: removeOption.bind(this) }\"></ng-container>\n </div>\n <ng-container *ngIf=\"isVisibleClearIcon\">\n <TimesIcon *ngIf=\"!clearIconTemplate\" [styleClass]=\"'p-multiselect-clear-icon'\" (click)=\"clear($event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"clearIconTemplate\" class=\"p-multiselect-clear-icon\" (click)=\"clear($event)\" [attr.data-pc-section]=\"'clearicon'\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"clearIconTemplate\"></ng-template>\n </span>\n </ng-container>\n </div>\n <div class=\"p-multiselect-trigger\">\n <ng-container *ngIf=\"!dropdownIconTemplate\">\n <span *ngIf=\"dropdownIcon\" class=\"p-multiselect-trigger-icon\" [ngClass]=\"dropdownIcon\" [attr.data-pc-section]=\"'triggericon'\" [attr.aria-hidden]=\"true\"></span>\n <ChevronDownIcon *ngIf=\"!dropdownIcon\" [styleClass]=\"'p-multiselect-trigger-icon'\" [attr.data-pc-section]=\"'triggericon'\" [attr.aria-hidden]=\"true\" />\n </ng-container>\n <span *ngIf=\"dropdownIconTemplate\" class=\"p-multiselect-trigger-icon\" [attr.data-pc-section]=\"'triggericon'\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"dropdownIconTemplate\"></ng-template>\n </span>\n </div>\n <p-overlay\n #overlay\n [(visible)]=\"overlayVisible\"\n [options]=\"overlayOptions\"\n [target]=\"'@parent'\"\n [appendTo]=\"appendTo\"\n [autoZIndex]=\"autoZIndex\"\n [baseZIndex]=\"baseZIndex\"\n [showTransitionOptions]=\"showTransitionOptions\"\n [hideTransitionOptions]=\"hideTransitionOptions\"\n (onAnimationStart)=\"onOverlayAnimationStart($event)\"\n (onHide)=\"hide()\"\n >\n <ng-template pTemplate=\"content\">\n <div [ngClass]=\"'p-multiselect-panel p-component'\" [ngStyle]=\"panelStyle\" [class]=\"panelStyleClass\">\n <span\n #firstHiddenFocusableEl\n role=\"presentation\"\n [attr.aria-hidden]=\"'true'\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n [attr.tabindex]=\"0\"\n (focus)=\"onFirstHiddenFocus($event)\"\n [attr.data-p-hidden-accessible]=\"true\"\n [attr.data-p-hidden-focusable]=\"true\"\n >\n </span>\n <div class=\"p-multiselect-header\" *ngIf=\"showHeader\">\n <ng-content select=\"p-header\"></ng-content>\n <ng-container *ngTemplateOutlet=\"headerTemplate\"></ng-container>\n <ng-container *ngIf=\"filterTemplate; else builtInFilterElement\">\n <ng-container *ngTemplateOutlet=\"filterTemplate; context: { options: filterOptions }\"></ng-container>\n </ng-container>\n <ng-template #builtInFilterElement>\n <div\n class=\"p-checkbox p-component\"\n *ngIf=\"showToggleAll && !selectionLimit\"\n [ngClass]=\"{ 'p-checkbox-disabled': disabled || toggleAllDisabled }\"\n (click)=\"onToggleAll($event)\"\n (keydown)=\"onHeaderCheckboxKeyDown($event)\"\n >\n <div class=\"p-hidden-accessible\" [attr.data-p-hidden-accessible]=\"true\">\n <input\n #headerCheckbox\n type=\"checkbox\"\n [readonly]=\"readonly\"\n [attr.checked]=\"allSelected()\"\n (focus)=\"onHeaderCheckboxFocus()\"\n (blur)=\"onHeaderCheckboxBlur()\"\n [disabled]=\"disabled || toggleAllDisabled\"\n [attr.aria-label]=\"toggleAllAriaLabel\"\n />\n </div>\n <div class=\"p-checkbox-box\" role=\"checkbox\" [attr.aria-checked]=\"allSelected()\" [ngClass]=\"{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }\">\n <ng-container *ngIf=\"allSelected()\">\n <CheckIcon [styleClass]=\"'p-checkbox-icon'\" *ngIf=\"!checkIconTemplate\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"checkIconTemplate\" class=\"p-checkbox-icon\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"checkIconTemplate; context: { $implicit: allSelected() }\"></ng-template>\n </span>\n </ng-container>\n </div>\n </div>\n <div class=\"p-multiselect-filter-container\" *ngIf=\"filter\">\n <input\n #filterInput\n type=\"text\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.placeholder]=\"filterPlaceHolder\"\n role=\"searchbox\"\n [attr.aria-owns]=\"id + '_list'\"\n [attr.aria-activedescendant]=\"focusedOptionId\"\n [value]=\"_filterValue() || ''\"\n (input)=\"onFilterInputChange($event)\"\n (keydown)=\"onFilterKeyDown($event)\"\n (click)=\"onInputClick($event)\"\n (blur)=\"onFilterBlur($event)\"\n class=\"p-multiselect-filter p-inputtext p-component\"\n [disabled]=\"disabled\"\n [attr.placeholder]=\"filterPlaceHolder\"\n [attr.aria-label]=\"ariaFilterLabel\"\n />\n <SearchIcon [styleClass]=\"'p-multiselect-filter-icon'\" *ngIf=\"!filterIconTemplate\" />\n <span *ngIf=\"filterIconTemplate\" class=\"p-multiselect-filter-icon\">\n <ng-template *ngTemplateOutlet=\"filterIconTemplate\"></ng-template>\n </span>\n </div>\n\n <button class=\"p-multiselect-close p-link p-button-icon-only\" type=\"button\" (click)=\"close($event)\" pRipple [attr.aria-label]=\"closeAriaLabel\">\n <TimesIcon [styleClass]=\"'p-multiselect-close-icon'\" *ngIf=\"!closeIconTemplate\" />\n <span *ngIf=\"closeIconTemplate\" class=\"p-multiselect-close-icon\">\n <ng-template *ngTemplateOutlet=\"closeIconTemplate\"></ng-template>\n </span>\n </button>\n </ng-template>\n </div>\n <div class=\"p-multiselect-items-wrapper\" [style.max-height]=\"virtualScroll ? 'auto' : scrollHeight || 'auto'\">\n <p-scroller\n *ngIf=\"virtualScroll\"\n #scroller\n [items]=\"visibleOptions()\"\n [style]=\"{ height: scrollHeight }\"\n [itemSize]=\"virtualScrollItemSize || _itemSize\"\n [autoSize]=\"true\"\n [tabindex]=\"-1\"\n [lazy]=\"lazy\"\n (onLazyLoad)=\"onLazyLoad.emit($event)\"\n [options]=\"virtualScrollOptions\"\n >\n <ng-template pTemplate=\"content\" let-items let-scrollerOptions=\"options\">\n <ng-container *ngTemplateOutlet=\"buildInItems; context: { $implicit: items, options: scrollerOptions }\"></ng-container>\n </ng-template>\n <ng-container *ngIf=\"loaderTemplate\">\n <ng-template pTemplate=\"loader\" let-scrollerOptions=\"options\">\n <ng-container *ngTemplateOutlet=\"loaderTemplate; context: { options: scrollerOptions }\"></ng-container>\n </ng-template>\n </ng-container>\n </p-scroller>\n <ng-container *ngIf=\"!virtualScroll\">\n <ng-container *ngTemplateOutlet=\"buildInItems; context: { $implicit: visibleOptions(), options: {} }\"></ng-container>\n </ng-container>\n\n <ng-template #buildInItems let-items let-scrollerOptions=\"options\">\n <ul #items class=\"p-multiselect-items p-component\" [ngClass]=\"scrollerOptions.contentStyleClass\" [style]=\"scrollerOptions.contentStyle\" role=\"listbox\" aria-multiselectable=\"true\">\n <ng-template ngFor let-option [ngForOf]=\"items\" let-i=\"index\">\n <ng-container *ngIf=\"isOptionGroup(option)\">\n <li [attr.id]=\"id + '_' + getOptionIndex(i, scrollerOptions)\" class=\"p-multiselect-item-group\" [ngStyle]=\"{ height: scrollerOptions.itemSize + 'px' }\" role=\"option\">\n <span *ngIf=\"!groupTemplate\">{{ getOptionGroupLabel(option.optionGroup) }}</span>\n <ng-container *ngTemplateOutlet=\"groupTemplate; context: { $implicit: option.optionGroup }\"></ng-container>\n </li>\n </ng-container>\n <ng-container *ngIf=\"!isOptionGroup(option)\">\n <p-multiSelectItem\n [id]=\"id + '_' + getOptionIndex(i, scrollerOptions)\"\n [option]=\"option\"\n [selected]=\"isSelected(option)\"\n [label]=\"getOptionLabel(option)\"\n [disabled]=\"isOptionDisabled(option)\"\n [template]=\"itemTemplate\"\n [checkIconTemplate]=\"checkIconTemplate\"\n [itemSize]=\"scrollerOptions.itemSize\"\n [focused]=\"focusedOptionIndex() === getOptionIndex(i, scrollerOptions)\"\n [ariaPosInset]=\"getAriaPosInset(getOptionIndex(i, scrollerOptions))\"\n [ariaSetSize]=\"ariaSetSize\"\n (onClick)=\"onOptionSelect($event, false, getOptionIndex(i, scrollerOptions))\"\n (onMouseEnter)=\"onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))\"\n ></p-multiSelectItem>\n </ng-container>\n </ng-template>\n\n <li *ngIf=\"hasFilter() && isEmpty()\" class=\"p-multiselect-empty-message\" [ngStyle]=\"{ height: scrollerOptions.itemSize + 'px' }\">\n <ng-container *ngIf=\"!emptyFilterTemplate && !emptyTemplate; else emptyFilter\">\n {{ emptyFilterMessageLabel }}\n </ng-container>\n <ng-container #emptyFilter *ngTemplateOutlet=\"emptyFilterTemplate || emptyTemplate\"></ng-container>\n </li>\n <li *ngIf=\"!hasFilter() && isEmpty()\" class=\"p-multiselect-empty-message\" [ngStyle]=\"{ height: scrollerOptions.itemSize + 'px' }\">\n <ng-container *ngIf=\"!emptyTemplate; else empty\">\n {{ emptyMessageLabel }}\n </ng-container>\n <ng-container #empty *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n </li>\n </ul>\n </ng-template>\n </div>\n <div class=\"p-multiselect-footer\" *ngIf=\"footerFacet || footerTemplate\">\n <ng-content select=\"p-footer\"></ng-content>\n <ng-container *ngTemplateOutlet=\"footerTemplate\"></ng-container>\n </div>\n\n <span\n #lastHiddenFocusableEl\n role=\"presentation\"\n [attr.aria-hidden]=\"true\"\n class=\"p-hidden-accessible p-hidden-focusable\"\n [attr.tabindex]=\"0\"\n (focus)=\"onLastHiddenFocus($event)\"\n [attr.data-p-hidden-accessible]=\"true\"\n [attr.data-p-hidden-focusable]=\"true\"\n ></span>\n </div>\n </ng-template>\n </p-overlay>\n </div>\n `,\n host: {\n class: 'p-element p-inputwrapper',\n '[class.p-inputwrapper-focus]': 'focused || overlayVisible',\n '[class.p-inputwrapper-filled]': 'filled'\n },\n providers: [MULTISELECT_VALUE_ACCESSOR],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['./multiselect.css']\n})\nexport class MultiSelect implements OnInit, AfterViewInit, AfterContentInit, AfterViewChecked, ControlValueAccessor {\n /**\n * Unique identifier of the component\n * @group Props\n */\n @Input() id: string | undefined;\n /**\n * Defines a string that labels the input for accessibility.\n * @group Props\n */\n @Input() ariaLabel: string | undefined;\n /**\n * Inline style of the element.\n * @group Props\n */\n @Input() style: { [klass: string]: any } | null | undefined;\n /**\n * Style class of the element.\n * @group Props\n */\n @Input() styleClass: string | undefined;\n /**\n * Inline style of the overlay panel.\n * @group Props\n */\n @Input() panelStyle: any;\n /**\n * Style class of the overlay panel element.\n * @group Props\n */\n @Input() panelStyleClass: string | undefined;\n /**\n * Identifier of the focus input to match a label defined for the component.\n * @group Props\n */\n @Input() inputId: string | undefined;\n /**\n * When present, it specifies that the element should be disabled.\n * @group Props\n */\n @Input() disabled: boolean | undefined;\n /**\n * When present, it specifies that the component cannot be edited.\n * @group Props\n */\n @Input() readonly: boolean | undefined;\n /**\n * Whether to display options as grouped when nested options are provided.\n * @group Props\n */\n @Input() group: boolean | undefined;\n /**\n * When specified, displays an input field to filter the items on keyup.\n * @group Props\n */\n @Input() filter: boolean = true;\n /**\n * Defines placeholder of the filter input.\n * @group Props\n */\n @Input() filterPlaceHolder: string | undefined;\n /**\n * Locale to use in filtering. The default locale is the host environment's current locale.\n * @group Props\n */\n @Input() filterLocale: string | undefined;\n /**\n * Specifies the visibility of the options panel.\n * @group Props\n */\n @Input() overlayVisible: boolean | undefined;\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n @Input() tabindex: number | undefined = 0;\n /**\n * Target element to attach the overlay, valid values are \"body\" or a local ng-template variable of another element (note: use binding with brackets for template variables, e.g. [appendTo]=\"mydiv\" for a div element having #mydiv as variable name).\n * @group Props\n */\n @Input() appendTo: HTMLElement | ElementRef | TemplateRef<any> | string | null | undefined | any;\n /**\n * A property to uniquely identify a value in options.\n * @group Props\n */\n @Input() dataKey: string | undefined;\n /**\n * Name of the input element.\n * @group Props\n */\n @Input() name: string | undefined;\n /**\n * Establishes relationships between the component and label(s) where its value should be one or more element IDs.\n * @group Props\n */\n @Input() ariaLabelledBy: string | undefined;\n /**\n * Whether to show labels of selected item labels or use default label.\n * @group Props\n * @defaultValue true\n */\n @Input() set displaySelectedLabel(val: boolean) {\n this._displaySelectedLabel = val;\n }\n get displaySelectedLabel(): boolean {\n return this._displaySelectedLabel;\n }\n /**\n * Decides how many selected item labels to show at most.\n * @group Props\n * @defaultValue 3\n */\n @Input() set maxSelectedLabels(val: number | null | undefined) {\n this._maxSelectedLabels = val;\n }\n get maxSelectedLabels(): number | null | undefined {\n return this._maxSelectedLabels;\n }\n /**\n * Decides how many selected item labels to show at most.\n * @group Props\n */\n @Input() selectionLimit: number | undefined;\n /**\n * Label to display after exceeding max selected labels e.g. ({0} items selected), defaults \"ellipsis\" keyword to indicate a text-overflow.\n * @group Props\n */\n @Input() selectedItemsLabel: string = '{0} items selected';\n /**\n * Whether to show the checkbox at header to toggle all items at once.\n * @group Props\n */\n @Input() showToggleAll: boolean = true;\n /**\n * Text to display when filtering does not return any results.\n * @group Props\n */\n @Input() emptyFilterMessage: string = '';\n /**\n * Text to display when there is no data. Defaults to global value in i18n translation configuration.\n * @group Props\n */\n @Input() emptyMessage: string = '';\n /**\n * Clears the filter value when hiding the dropdown.\n * @group Props\n */\n @Input() resetFilterOnHide: boolean = false;\n /**\n * Icon class of the dropdown icon.\n * @group Props\n */\n @Input() dropdownIcon: string | undefined;\n /**\n * Name of the label field of an option.\n * @group Props\n */\n @Input() optionLabel: string | undefined;\n /**\n * Name of the value field of an option.\n * @group Props\n */\n @Input() optionValue: string | undefined;\n /**\n * Name of the disabled field of an option.\n * @group Props\n */\n @Input() optionDisabled: string | undefined;\n /**\n * Name of the label field of an option group.\n * @group Props\n */\n @Input() optionGroupLabel: string | undefined = 'label';\n /**\n * Name of the options field of an option group.\n * @group Props\n */\n @Input() optionGroupChildren: string = 'items';\n /**\n * Whether to show the header.\n * @group Props\n */\n @Input() showHeader: boolean = true;\n /**\n * When filtering is enabled, filterBy decides which field or fields (comma separated) to search against.\n * @group Props\n */\n @Input() filterBy: string | undefined;\n /**\n * Height of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.\n * @group Props\n */\n @Input() scrollHeight: string = '200px';\n /**\n * Defines if data is loaded and interacted with in lazy manner.\n * @group Props\n */\n @Input() lazy: boolean = false;\n /**\n * Whether the data should be loaded on demand during scroll.\n * @group Props\n */\n @Input() virtualScroll: boolean | undefined;\n /**\n * Height of an item in the list for VirtualScrolling.\n * @group Props\n */\n @Input() virtualScrollItemSize: number | undefined;\n /**\n * Whether to use the scroller feature. The properties of scroller component can be used like an object in it.\n * @group Props\n */\n @Input() virtualScrollOptions: ScrollerOptions | undefined;\n /**\n * Whether to use overlay API feature. The properties of overlay API can be used like an object in it.\n * @group Props\n */\n @Input() overlayOptions: OverlayOptions | undefined;\n /**\n * Defines a string that labels the filter input.\n * @group Props\n */\n @Input() ariaFilterLabel: string | undefined;\n /**\n * Defines how the items are filtered.\n * @group Props\n */\n @Input() filterMatchMode: 'contains' | 'startsWith' | 'endsWith' | 'equals' | 'notEquals' | 'in' | 'lt' | 'lte' | 'gt' | 'gte' = 'contains';\n /**\n * Advisory information to display in a tooltip on hover.\n * @group Props\n */\n @Input() tooltip: string = '';\n /**\n * Position of the tooltip.\n * @group Props\n */\n @Input() tooltipPosition: 'top' | 'left' | 'right' | 'bottom' = 'right';\n /**\n * Type of CSS position.\n * @group Props\n */\n @Input() tooltipPositionStyle: string = 'absolute';\n /**\n * Style class of the tooltip.\n * @group Props\n */\n @Input() tooltipStyleClass: string | undefined;\n /**\n * Applies focus to the filter element when the overlay is shown.\n * @group Props\n */\n @Input() autofocusFilter: boolean = true;\n /**\n * Defines how the selected items are displayed.\n * @group Props\n */\n @Input() display: string | 'comma' | 'chip' = 'comma';\n /**\n * Defines the autocomplete is active.\n * @group Props\n */\n @Input() autocomplete: string = 'off';\n /**\n * When enabled, a clear icon is displayed to clear the value.\n * @group Props\n */\n @Input() showClear: boolean = false;\n /**\n * @deprecated since v14.2.0, use overlayOptions property instead.\n * Whether to automatically manage layering.\n * @group Props\n */\n @Input() get autoZIndex(): boolean | undefined {\n return this._autoZIndex;\n }\n set autoZIndex(val: boolean | undefined) {\n this._autoZIndex = val;\n console.warn('The autoZIndex property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * @deprecated since v14.2.0, use overlayOptions property instead.\n * Base zIndex value to use in layering.\n * @group Props\n */\n @Input() get baseZIndex(): number | undefined {\n return this._baseZIndex;\n }\n set baseZIndex(val: number | undefined) {\n this._baseZIndex = val;\n console.warn('The baseZIndex property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * Transition options of the show animation.\n * @group Props\n * @deprecated since v14.2.0, use overlayOptions property instead.\n */\n @Input() get showTransitionOptions(): string | undefined {\n return this._showTransitionOptions;\n }\n set showTransitionOptions(val: string | undefined) {\n this._showTransitionOptions = val;\n console.warn('The showTransitionOptions property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * Transition options of the hide animation.\n * @group Props\n * @deprecated since v14.2.0, use overlayOptions property instead.\n */\n @Input() get hideTransitionOptions(): string | undefined {\n return this._hideTransitionOptions;\n }\n set hideTransitionOptions(val: string | undefined) {\n this._hideTransitionOptions = val;\n console.warn('The hideTransitionOptions property is deprecated since v14.2.0, use overlayOptions property instead.');\n }\n /**\n * Label to display when there are no selections.\n * @group Props\n * @deprecated Use placeholder instead.\n */\n @Input() set defaultLabel(val: string | undefined) {\n this._defaultLabel = val;\n console.warn('defaultLabel property is deprecated since 16.6.0, use placeholder instead');\n }\n get defaultLabel(): string | undefined {\n return this._defaultLabel;\n }\n /**\n * Label to display when there are no selections.\n * @group Props\n */\n @Input() set placeholder(val: string | undefined) {\n this._placeholder = val;\n }\n get placeholder(): string | undefined {\n return this._placeholder;\n }\n /**\n * An array of objects to display as the available options.\n * @group Props\n */\n @Input() get options(): any[] | undefined {\n const options = this._options();\n return options;\n }\n set options(val: any[] | undefined) {\n if (!ObjectUtils.deepEquals(this._options(), val)) {\n this._options.set(val);\n }\n }\n /**\n * When specified, filter displays with this value.\n * @group Props\n */\n @Input() get filterValue(): string | undefined | null {\n return this._filterValue();\n }\n set filterValue(val: string | undefined | null) {\n this._filterValue.set(val);\n }\n /**\n * Item size of item to be virtual scrolled.\n * @group Props\n * @deprecated use virtualScrollItemSize property instead.\n */\n @Input() get itemSize(): number | undefined {\n return this._itemSize;\n }\n set itemSize(val: number | undefined) {\n this._itemSize = val;\n console.warn('The itemSize property is deprecated, use virtualScrollItemSize property instead.');\n }\n /**\n * Whether all data is selected.\n * @group Props\n */\n @Input() get selectAll(): boolean | undefined | null {\n return this._selectAll;\n }\n set selectAll(value: boolean | undefined | null) {\n this._selectAll = value;\n }\n /**\n * Fields used when filtering the options, defaults to optionLabel.\n * @group Props\n */\n @Input() focusOnHover: boolean = false;\n /**\n * Fields used when filtering the options, defaults to optionLabel.\n * @group Props\n */\n @Input() filterFields: any[] | undefined;\n /**\n * Determines if the option will be selected on focus.\n * @group Props\n */\n @Input() selectOnFocus: boolean = false;\n /**\n * Whether to focus on the first visible or selected element when the overlay panel is shown.\n * @group Props\n */\n @Input() autoOptionFocus: boolean = true;\n /**\n * Callback to invoke when value changes.\n * @param {MultiSelectChangeEvent} event - Custom change event.\n * @group Emits\n */\n @Output() onChange: EventEmitter<MultiSelectChangeEvent> = new EventEmitter<MultiSelectChangeEvent>();\n /**\n * Callback to invoke when data is filtered.\n * @param {MultiSelectFilterEvent} event - Custom filter event.\n * @group Emits\n */\n @Output() onFilter: EventEmitter<MultiSelectFilterEvent> = new EventEmitter<MultiSelectFilterEvent>();\n /**\n * Callback to invoke when multiselect receives focus.\n * @param {MultiSelectFocusEvent} event - Custom focus event.\n * @group Emits\n */\n @Output() onFocus: EventEmitter<MultiSelectFocusEvent> = new EventEmitter<MultiSelectFocusEvent>();\n /**\n * Callback to invoke when multiselect loses focus.\n * @param {MultiSelectBlurEvent} event - Custom blur event.\n * @group Emits\n */\n @Output() onBlur: EventEmitter<MultiSelectBlurEvent> = new EventEmitter<MultiSelectBlurEvent>();\n /**\n * Callback to invoke when component is clicked.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onClick: EventEmitter<Event> = new EventEmitter<Event>();\n /**\n * Callback to invoke when input field is cleared.\n * @group Emits\n */\n @Output() onClear: EventEmitter<void> = new EventEmitter<void>();\n /**\n * Callback to invoke when overlay panel becomes visible.\n * @group Emits\n */\n @Output() onPanelShow: EventEmitter<void> = new EventEmitter<void>();\n /**\n * Callback to invoke when overlay panel becomes hidden.\n * @group Emits\n */\n @Output() onPanelHide: EventEmitter<void> = new EventEmitter<void>();\n /**\n * Callback to invoke in lazy mode to load new data.\n * @param {MultiSelectLazyLoadEvent} event - Lazy load event.\n * @group Emits\n */\n @Output() onLazyLoad: EventEmitter<MultiSelectLazyLoadEvent> = new EventEmitter<MultiSelectLazyLoadEvent>();\n /**\n * Callback to invoke in lazy mode to load new data.\n * @param {MultiSelectRemoveEvent} event - Remove event.\n * @group Emits\n */\n @Output() onRemove: EventEmitter<MultiSelectRemoveEvent> = new EventEmitter<MultiSelectRemoveEvent>();\n /**\n * Callback to invoke when all data is selected.\n * @param {MultiSelectSelectAllChangeEvent} event - Custom select event.\n * @group Emits\n */\n @Output() onSelectAllChange: EventEmitter<MultiSelectSelectAllChangeEvent> = new EventEmitter<MultiSelectSelectAllChangeEvent>();\n\n @ViewChild('container') containerViewChild: Nullable<ElementRef>;\n\n @ViewChild('overlay') overlayViewChild: Nullable<Overlay>;\n\n @ViewChild('filterInput') filterInputChild: Nullable<ElementRef>;\n\n @ViewChild('focusInput') focusInputViewChild: Nullable<ElementRef>;\n\n @ViewChild('items') itemsViewChild: Nullable<ElementRef>;\n\n @ViewChild('scroller') scroller: Nullable<Scroller>;\n\n @ViewChild('lastHiddenFocusableEl') lastHiddenFocusableElementOnOverlay: Nullable<ElementRef>;\n\n @ViewChild('firstHiddenFocusableEl') firstHiddenFocusableElementOnOverlay: Nullable<ElementRef>;\n\n @ViewChild('headerCheckbox') headerCheckboxViewChild: Nullable<ElementRef>;\n\n @ContentChild(Footer) footerFacet: any;\n\n @ContentChild(Header) headerFacet: any;\n\n @ContentChildren(PrimeTemplate) templates: Nullable<QueryList<PrimeTemplate>>;\n\n searchValue: Nullable<string>;\n\n searchTimeout: any;\n\n _selectAll: boolean | undefined | null = null;\n\n _autoZIndex: boolean | undefined;\n\n _baseZIndex: number | undefined;\n\n _showTransitionOptions: string | undefined;\n\n _hideTransitionOptions: string | undefined;\n\n _defaultLabel: string | undefined;\n\n _placeholder: string | undefined;\n\n _itemSize: number | undefined;\n\n _selectionLimit: number | undefined;\n\n value: any[];\n\n public _filteredOptions: any[] | undefined | null;\n\n public onModelChange: Function = () => {};\n\n public onModelTouched: Function = () => {};\n\n public valuesAsString: string | undefined;\n\n public focus: boolean | undefined;\n\n public filtered: boolean | undefined;\n\n public itemTemplate: TemplateRef<any> | undefined;\n\n public groupTemplate: TemplateRef<any> | undefined;\n\n public loaderTemplate: TemplateRef<any> | undefined;\n\n public headerTemplate: TemplateRef<any> | undefined;\n\n public filterTemplate: TemplateRef<any> | undefined;\n\n public footerTemplate: TemplateRef<any> | undefined;\n\n public emptyFilterTemplate: TemplateRef<any> | undefined;\n\n public emptyTemplate: TemplateRef<any> | undefined;\n\n public selectedItemsTemplate: TemplateRef<any> | undefined;\n\n checkIconTemplate: TemplateRef<any> | undefined;\n\n filterIconTemplate: TemplateRef<any> | undefined;\n\n removeTokenIconTemplate: TemplateRef<any> | undefined;\n\n closeIconTemplate: TemplateRef<any> | undefined;\n\n clearIconTemplate: TemplateRef<any> | undefined;\n\n dropdownIconTemplate: TemplateRef<any> | undefined;\n\n public headerCheckboxFocus: boolean | undefined;\n\n filterOptions: MultiSelectFilterOptions | undefined;\n\n preventModelTouched: boolean | undefined;\n\n preventDocumentDefault: boolean | undefined;\n\n focused: boolean = false;\n\n itemsWrapper: any;\n\n _displaySelectedLabel: boolean = true;\n\n _maxSelectedLabels: number = 3;\n\n modelValue = signal<any>(null);\n\n _filterValue = signal<any>(null);\n\n _options = signal<any[]>(null);\n\n startRangeIndex = signal<number>(-1);\n\n focusedOptionIndex = signal<number>(-1);\n\n selectedOptions: any;\n\n get containerClass() {\n return {\n 'p-multiselect p-component p-inputwrapper': true,\n 'p-disabled': this.disabled,\n 'p-multiselect-clearable': this.showClear && !this.disabled,\n 'p-multiselect-chip': this.display === 'chip',\n 'p-focus': this.focused\n };\n }\n\n get inputClass() {\n return {\n 'p-multiselect-label p-inputtext': true,\n 'p-placeholder': (this.placeholder || this.defaultLabel) && (this.label() === this.placeholder || this.label() === this.defaultLabel),\n 'p-multiselect-label-empty': !this.selectedItemsTemplate && (this.label() === 'p-emptylabel' || this.label().length === 0)\n };\n }\n\n get panelClass() {\n return {\n 'p-multiselect-panel p-component': true,\n 'p-input-filled': this.config.inputStyle === 'filled',\n 'p-ripple-disabled': this.config.ripple === false\n };\n }\n\n get labelClass() {\n return {\n 'p-multiselect-label': true,\n 'p-placeholder': this.label() === this.placeholder || this.label() === this.defaultLabel,\n 'p-multiselect-label-empty': !this.placeholder && !this.defaultLabel && (!this.modelValue() || this.modelValue().length === 0)\n };\n }\n\n get emptyMessageLabel(): string {\n return this.emptyMessage || this.config.getTranslation(TranslationKeys.EMPTY_MESSAGE);\n }\n\n get emptyFilterMessageLabel(): string {\n return this.emptyFilterMessage || this.config.getTranslation(TranslationKeys.EMPTY_FILTER_MESSAGE);\n }\n\n get filled(): boolean {\n if (typeof this.modelValue() === 'string') return !!this.modelValue();\n\n return ObjectUtils.isNotEmpty(this.modelValue());\n }\n\n get isVisibleClearIcon(): boolean | undefined {\n return this.modelValue() != null && this.modelValue() !== '' && ObjectUtils.isNotEmpty(this.modelValue()) && this.showClear && !this.disabled && this.filled;\n }\n\n get toggleAllAriaLabel() {\n return this.config.translation.aria ? this.config.translation.aria[this.allSelected() ? 'selectAll' : 'unselectAll'] : undefined;\n }\n\n get closeAriaLabel() {\n return this.config.translation.aria ? this.config.translation.aria.close : undefined;\n }\n\n visibleOptions = computed(() => {\n const options = this.group ? this.flatOptions(this.options) : this.options || [];\n\n if (this._filterValue()) {\n const filteredOptions = this.filterService.filter(options, this.searchFields(), this._filterValue(), this.filterMatchMode, this.filterLocale);\n if (this.group) {\n const optionGroups = this.options || [];\n const filtered = [];\n\n optionGroups.forEach((group) => {\n const groupChildren = this.getOptionGroupChildren(group);\n const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item));\n\n if (filteredItems.length > 0) filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] });\n });\n\n return this.flatOptions(filtered);\n }\n\n return filteredOptions;\n }\n return options;\n });\n\n label = computed(() => {\n let label;\n const modelValue = this.modelValue();\n\n if (modelValue && modelValue.length && this.displaySelectedLabel) {\n if (ObjectUtils.isNotEmpty(this.maxSelectedLabels) && modelValue.length > this.maxSelectedLabels) {\n return this.getSelectedItemsLabel();\n } else {\n label = '';\n\n for (let i = 0; i < modelValue.length; i++) {\n if (i !== 0) {\n label += ', ';\n }\n\n label += this.getLabelByValue(modelValue[i]);\n }\n }\n } else {\n label = this.placeholder || this.defaultLabel || '';\n }\n return label;\n });\n\n chipSelectedItems = computed(() => {\n return ObjectUtils.isNotEmpty(this.maxSelectedLabels) && this.modelValue() && this.modelValue().length > this.maxSelectedLabels ? this.modelValue().slice(0, this.maxSelectedLabels) : this.modelValue();\n });\n\n constructor(public el: ElementRef, public renderer: Renderer2, public cd: ChangeDetectorRef, public zone: NgZone, public filterService: FilterService, public config: PrimeNGConfig, public overlayService: OverlayService) {\n effect(() => {\n const modelValue = this.modelValue();\n\n const visibleOptions = this.visibleOptions();\n if (visibleOptions && ObjectUtils.isNotEmpty(visibleOptions)) {\n if (this.optionValue && this.optionLabel && modelValue) {\n this.selectedOptions = visibleOptions.filter((option) => modelValue.includes(option[this.optionLabel]) || modelValue.includes(option[this.optionValue]));\n } else {\n this.selectedOptions = modelValue;\n }\n this.cd.markForCheck();\n }\n });\n }\n\n ngOnInit() {\n this.id = this.id || UniqueComponentId();\n this.autoUpdateModel();\n\n if (this.filterBy) {\n this.filterOptions = {\n filter: (value) => this.onFilterInputChange(value),\n reset: () => this.resetFilter()\n };\n }\n }\n\n maxSelectionLimitReached() {\n return this.selectionLimit && this.modelValue() && this.modelValue().length === this.selectionLimit;\n }\n\n ngAfterContentInit() {\n (this.templates as QueryList<PrimeTemplate>).forEach((item) => {\n switch (item.getType()) {\n case 'item':\n this.itemTemplate = item.template;\n break;\n\n case 'group':\n this.groupTemplate = item.template;\n break;\n\n case 'selectedItems':\n this.selectedItemsTemplate = item.template;\n break;\n\n case 'header':\n this.headerTemplate = item.template;\n break;\n\n case 'filter':\n this.filterTemplate = item.template;\n break;\n\n case 'emptyfilter':\n this.emptyFilterTemplate = item.template;\n break;\n\n case 'empty':\n this.emptyTemplate = item.template;\n break;\n\n case 'footer':\n this.footerTemplate = item.template;\n break;\n\n case 'loader':\n this.loaderTemplate = item.template;\n