primeng
Version:
[](https://badge.fury.io/js/primeng) [](https://www.npmjs.com/package/primeng) [
{"version":3,"file":"primeng-autocomplete.mjs","sources":["../../src/app/components/autocomplete/autocomplete.ts","../../src/app/components/autocomplete/primeng-autocomplete.ts"],"sourcesContent":["import { animate, AnimationEvent, style, transition, trigger } from '@angular/animations';\nimport { CommonModule, DOCUMENT } from '@angular/common';\nimport {\n AfterContentInit,\n AfterViewChecked,\n booleanAttribute,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n computed,\n ContentChildren,\n effect,\n ElementRef,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n NgModule,\n NgZone,\n numberAttribute,\n OnDestroy,\n Output,\n QueryList,\n Renderer2,\n signal,\n TemplateRef,\n ViewChild,\n ViewEncapsulation\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { OverlayOptions, OverlayService, PrimeNGConfig, PrimeTemplate, SharedModule, TranslationKeys } from 'primeng/api';\nimport { AutoFocusModule } from 'primeng/autofocus';\nimport { ButtonModule } from 'primeng/button';\nimport { ConnectedOverlayScrollHandler, DomHandler } from 'primeng/dom';\nimport { InputTextModule } from 'primeng/inputtext';\nimport { Overlay, OverlayModule } from 'primeng/overlay';\nimport { RippleModule } from 'primeng/ripple';\nimport { Scroller, ScrollerModule } from 'primeng/scroller';\nimport { ScrollerOptions } from 'primeng/api';\nimport { ObjectUtils, UniqueComponentId } from 'primeng/utils';\nimport { TimesCircleIcon } from 'primeng/icons/timescircle';\nimport { SpinnerIcon } from 'primeng/icons/spinner';\nimport { TimesIcon } from 'primeng/icons/times';\nimport { ChevronDownIcon } from 'primeng/icons/chevrondown';\nimport { Nullable, VoidListener } from 'primeng/ts-helpers';\nimport { AutoCompleteCompleteEvent, AutoCompleteDropdownClickEvent, AutoCompleteLazyLoadEvent, AutoCompleteSelectEvent, AutoCompleteUnselectEvent } from './autocomplete.interface';\n\nexport const AUTOCOMPLETE_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => AutoComplete),\n multi: true\n};\n/**\n * AutoComplete is an input component that provides real-time suggestions when being typed.\n * @group Components\n */\n@Component({\n selector: 'p-autoComplete',\n template: `\n <div #container [ngClass]=\"containerClass\" [ngStyle]=\"style\" [class]=\"styleClass\" (click)=\"onContainerClick($event)\">\n <input\n *ngIf=\"!multiple\"\n #focusInput\n pAutoFocus\n [autofocus]=\"autofocus\"\n [ngClass]=\"inputClass\"\n [ngStyle]=\"inputStyle\"\n [class]=\"inputStyleClass\"\n [type]=\"type\"\n [attr.value]=\"inputValue()\"\n [attr.id]=\"inputId\"\n [autocomplete]=\"autocomplete\"\n [required]=\"required\"\n [name]=\"name\"\n aria-autocomplete=\"list\"\n role=\"combobox\"\n [attr.placeholder]=\"placeholder\"\n [attr.size]=\"size\"\n [attr.maxlength]=\"maxlength\"\n [tabindex]=\"!disabled ? tabindex : -1\"\n [readonly]=\"readonly\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledBy\"\n [attr.aria-required]=\"required\"\n [attr.aria-expanded]=\"overlayVisible ?? false\"\n [attr.aria-controls]=\"overlayVisible ? id + '_list' : null\"\n [attr.aria-activedescendant]=\"focused ? focusedOptionId : undefined\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeyDown($event)\"\n (change)=\"onInputChange($event)\"\n (focus)=\"onInputFocus($event)\"\n (blur)=\"onInputBlur($event)\"\n (paste)=\"onInputPaste($event)\"\n (keyup)=\"onInputKeyUp($event)\"\n />\n <ng-container *ngIf=\"filled && !disabled && showClear && !loading\">\n <TimesIcon *ngIf=\"!clearIconTemplate\" [styleClass]=\"'p-autocomplete-clear-icon'\" (click)=\"clear()\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"clearIconTemplate\" class=\"p-autocomplete-clear-icon\" (click)=\"clear()\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"clearIconTemplate\"></ng-template>\n </span>\n </ng-container>\n\n <ul\n *ngIf=\"multiple\"\n #multiContainer\n [ngClass]=\"multiContainerClass\"\n [tabindex]=\"-1\"\n role=\"listbox\"\n [attr.aria-orientation]=\"'horizontal'\"\n [attr.aria-activedescendant]=\"focused ? focusedMultipleOptionId : undefined\"\n (focus)=\"onMultipleContainerFocus($event)\"\n (blur)=\"onMultipleContainerBlur($event)\"\n (keydown)=\"onMultipleContainerKeyDown($event)\"\n >\n <li\n #token\n *ngFor=\"let option of modelValue(); let i = index\"\n [ngClass]=\"{ 'p-autocomplete-token': true, 'p-focus': focusedMultipleOptionIndex() === i }\"\n [attr.id]=\"id + '_multiple_option_' + i\"\n role=\"option\"\n [attr.aria-label]=\"getOptionLabel(option)\"\n [attr.aria-setsize]=\"modelValue().length\"\n [attr.aria-posinset]=\"i + 1\"\n [attr.aria-selected]=\"true\"\n >\n <ng-container *ngTemplateOutlet=\"selectedItemTemplate; context: { $implicit: option }\"></ng-container>\n <span *ngIf=\"!selectedItemTemplate\" class=\"p-autocomplete-token-label\">{{ getOptionLabel(option) }}</span>\n <span class=\"p-autocomplete-token-icon\" (click)=\"!readonly ? removeOption($event, i) : ''\">\n <TimesCircleIcon [styleClass]=\"'p-autocomplete-token-icon'\" *ngIf=\"!removeIconTemplate\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"removeIconTemplate\" class=\"p-autocomplete-token-icon\" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"removeIconTemplate\"></ng-template>\n </span>\n </span>\n </li>\n <li class=\"p-autocomplete-input-token\" role=\"option\">\n <input\n #focusInput\n pAutoFocus\n [autofocus]=\"autofocus\"\n [ngClass]=\"inputClass\"\n [ngStyle]=\"inputStyle\"\n [class]=\"inputStyleClass\"\n [attr.type]=\"type\"\n [attr.id]=\"inputId\"\n [autocomplete]=\"autocomplete\"\n [required]=\"required\"\n [attr.name]=\"name\"\n role=\"combobox\"\n [attr.placeholder]=\"!filled ? placeholder : null\"\n [attr.size]=\"size\"\n aria-autocomplete=\"list\"\n [attr.maxlength]=\"maxlength\"\n [tabindex]=\"!disabled ? tabindex : -1\"\n [readonly]=\"readonly\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"ariaLabel\"\n [attr.aria-labelledby]=\"ariaLabelledBy\"\n [attr.aria-required]=\"required\"\n [attr.aria-expanded]=\"overlayVisible ?? false\"\n [attr.aria-controls]=\"overlayVisible ? id + '_list' : null\"\n [attr.aria-activedescendant]=\"focused ? focusedOptionId : undefined\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeyDown($event)\"\n (change)=\"onInputChange($event)\"\n (focus)=\"onInputFocus($event)\"\n (blur)=\"onInputBlur($event)\"\n (paste)=\"onInputPaste($event)\"\n (keyup)=\"onInputKeyUp($event)\"\n />\n </li>\n </ul>\n <ng-container *ngIf=\"loading\">\n <SpinnerIcon *ngIf=\"!loadingIconTemplate\" [styleClass]=\"'p-autocomplete-loader'\" [spin]=\"true\" [attr.aria-hidden]=\"true\" />\n <span *ngIf=\"loadingIconTemplate\" class=\"p-autocomplete-loader pi-spin \" [attr.aria-hidden]=\"true\">\n <ng-template *ngTemplateOutlet=\"loadingIconTemplate\"></ng-template>\n </span>\n </ng-container>\n <button #ddBtn type=\"button\" pButton [attr.aria-label]=\"dropdownAriaLabel\" class=\"p-autocomplete-dropdown p-button-icon-only\" [disabled]=\"disabled\" pRipple (click)=\"handleDropdownClick($event)\" *ngIf=\"dropdown\" [attr.tabindex]=\"tabindex\">\n <span *ngIf=\"dropdownIcon\" [ngClass]=\"dropdownIcon\" [attr.aria-hidden]=\"true\"></span>\n <ng-container *ngIf=\"!dropdownIcon\">\n <ChevronDownIcon *ngIf=\"!dropdownIconTemplate\" />\n <ng-template *ngTemplateOutlet=\"dropdownIconTemplate\"></ng-template>\n </ng-container>\n </button>\n <p-overlay\n #overlay\n [(visible)]=\"overlayVisible\"\n [options]=\"overlayOptions\"\n [target]=\"'@parent'\"\n [appendTo]=\"appendTo\"\n [showTransitionOptions]=\"showTransitionOptions\"\n [hideTransitionOptions]=\"hideTransitionOptions\"\n (onAnimationStart)=\"onOverlayAnimationStart($event)\"\n (onHide)=\"hide()\"\n >\n <div [ngClass]=\"panelClass\" [style.max-height]=\"virtualScroll ? 'auto' : scrollHeight\" [ngStyle]=\"panelStyle\" [class]=\"panelStyleClass\">\n <ng-container *ngTemplateOutlet=\"headerTemplate\"></ng-container>\n <p-scroller\n *ngIf=\"virtualScroll\"\n #scroller\n [items]=\"visibleOptions()\"\n [style]=\"{ height: scrollHeight }\"\n [itemSize]=\"virtualScrollItemSize || _itemSize\"\n [autoSize]=\"true\"\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-autocomplete-items\" [ngClass]=\"scrollerOptions.contentStyleClass\" [style]=\"scrollerOptions.contentStyle\" role=\"listbox\" [attr.id]=\"id + '_list'\" [attr.aria-label]=\"listLabel\">\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-autocomplete-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 <li\n class=\"p-autocomplete-item\"\n pRipple\n [ngStyle]=\"{ height: scrollerOptions.itemSize + 'px' }\"\n [ngClass]=\"{ 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex() === getOptionIndex(i, scrollerOptions), 'p-disabled': isOptionDisabled(option) }\"\n [attr.id]=\"id + '_' + getOptionIndex(i, scrollerOptions)\"\n role=\"option\"\n [attr.aria-label]=\"getOptionLabel(option)\"\n [attr.aria-selected]=\"isSelected(option)\"\n [attr.aria-disabled]=\"isOptionDisabled(option)\"\n [attr.data-p-focused]=\"focusedOptionIndex() === getOptionIndex(i, scrollerOptions)\"\n [attr.aria-setsize]=\"ariaSetSize\"\n [attr.aria-posinset]=\"getAriaPosInset(getOptionIndex(i, scrollerOptions))\"\n (click)=\"onOptionSelect($event, option)\"\n (mouseenter)=\"onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))\"\n >\n <span *ngIf=\"!itemTemplate\">{{ getOptionLabel(option) }}</span>\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { $implicit: option, index: scrollerOptions.getOptions ? scrollerOptions.getOptions(i) : i }\"></ng-container>\n </li>\n </ng-container>\n </ng-template>\n <li *ngIf=\"!items || (items && items.length === 0 && showEmptyMessage)\" class=\"p-autocomplete-empty-message\" [ngStyle]=\"{ height: scrollerOptions.itemSize + 'px' }\" role=\"option\">\n <ng-container *ngIf=\"!emptyTemplate; else empty\">\n {{ searchResultMessageText }}\n </ng-container>\n <ng-container #empty *ngTemplateOutlet=\"emptyTemplate\"></ng-container>\n </li>\n </ul>\n </ng-template>\n <ng-container *ngTemplateOutlet=\"footerTemplate\"></ng-container>\n </div>\n <span role=\"status\" aria-live=\"polite\" class=\"p-hidden-accessible\">\n {{ selectedMessageText }}\n </span>\n </p-overlay>\n </div>\n `,\n host: {\n class: 'p-element p-inputwrapper',\n '[class.p-inputwrapper-filled]': 'filled',\n '[class.p-inputwrapper-focus]': '((focused && !disabled) || autofocus) || overlayVisible',\n '[class.p-autocomplete-clearable]': 'showClear && !disabled'\n },\n providers: [AUTOCOMPLETE_VALUE_ACCESSOR],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['./autocomplete.css']\n})\nexport class AutoComplete implements AfterViewChecked, AfterContentInit, OnDestroy, ControlValueAccessor {\n /**\n * Minimum number of characters to initiate a search.\n * @group Props\n */\n @Input({ transform: numberAttribute }) minLength: number = 1;\n /**\n * Delay between keystrokes to wait before sending a query.\n * @group Props\n */\n @Input({ transform: numberAttribute }) delay: number = 300;\n /**\n * Inline style of the component.\n * @group Props\n */\n @Input() style: { [klass: string]: any } | null | undefined;\n /**\n * Inline style of the overlay panel element.\n * @group Props\n */\n @Input() panelStyle: { [klass: string]: any } | null | undefined;\n /**\n * Style class of the component.\n * @group Props\n */\n @Input() styleClass: string | undefined;\n /**\n * Style class of the overlay panel element.\n * @group Props\n */\n @Input() panelStyleClass: string | undefined;\n /**\n * Inline style of the input field.\n * @group Props\n */\n @Input() inputStyle: { [klass: string]: any } | null | 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 * Inline style of the input field.\n * @group Props\n */\n @Input() inputStyleClass: string | undefined;\n /**\n * Hint text for the input field.\n * @group Props\n */\n @Input() placeholder: string | undefined;\n /**\n * When present, it specifies that the input cannot be typed.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) readonly: boolean | undefined;\n /**\n * When present, it specifies that the component should be disabled.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) disabled: boolean | undefined;\n /**\n * Maximum height of the suggestions panel.\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({ transform: booleanAttribute }) lazy: boolean = false;\n /**\n * Whether the data should be loaded on demand during scroll.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) virtualScroll: boolean | undefined;\n /**\n * Height of an item in the list for VirtualScrolling.\n * @group Props\n */\n @Input({ transform: numberAttribute }) 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 * Maximum number of character allows in the input field.\n * @group Props\n */\n @Input({ transform: (value: unknown) => numberAttribute(value, null) }) maxlength: number | undefined;\n /**\n * Name of the input element.\n * @group Props\n */\n @Input() name: string | undefined;\n /**\n * When present, it specifies that an input field must be filled out before submitting the form.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) required: boolean | undefined;\n /**\n * Size of the input field.\n * @group Props\n */\n @Input({ transform: numberAttribute }) size: number | undefined;\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 * When enabled, highlights the first item in the list by default.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autoHighlight: boolean | undefined;\n /**\n * When present, autocomplete clears the manual input if it does not match of the suggestions to force only accepting values from the suggestions.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) forceSelection: boolean | undefined;\n /**\n * Type of the input, defaults to \"text\".\n * @group Props\n */\n @Input() type: string = 'text';\n /**\n * Whether to automatically manage layering.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autoZIndex: boolean = true;\n /**\n * Base zIndex value to use in layering.\n * @group Props\n */\n @Input({ transform: numberAttribute }) baseZIndex: number = 0;\n /**\n * Defines a string that labels the input for accessibility.\n * @group Props\n */\n @Input() ariaLabel: string | undefined;\n /**\n * Defines a string that labels the dropdown button for accessibility.\n * @group Props\n */\n @Input() dropdownAriaLabel: string | undefined;\n /**\n * Specifies one or more IDs in the DOM that labels the input field.\n * @group Props\n */\n @Input() ariaLabelledBy: string | undefined;\n /**\n * Icon class of the dropdown icon.\n * @group Props\n */\n @Input() dropdownIcon: string | undefined;\n /**\n * Ensures uniqueness of selected items on multiple mode.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) unique: boolean = true;\n /**\n * Whether to display options as grouped when nested options are provided.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) group: boolean | undefined;\n /**\n * Whether to run a query when input receives focus.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) completeOnFocus: boolean = false;\n /**\n * When enabled, a clear icon is displayed to clear the value.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showClear: boolean = false;\n /**\n * Field of a suggested object to resolve and display.\n * @group Props\n * @deprecated use optionLabel property instead\n */\n @Input() field: string | undefined;\n /**\n * Displays a button next to the input field when enabled.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) dropdown: boolean | undefined;\n /**\n * Whether to show the empty message or not.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) showEmptyMessage: boolean | undefined = true;\n /**\n * Specifies the behavior dropdown button. Default \"blank\" mode sends an empty string and \"current\" mode sends the input value.\n * @group Props\n */\n @Input() dropdownMode: string = 'blank';\n /**\n * Specifies if multiple values can be selected.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) multiple: boolean | undefined;\n /**\n * Index of the element in tabbing order.\n * @group Props\n */\n @Input({ transform: numberAttribute }) tabindex: number | undefined;\n /**\n * A property to uniquely identify a value in options.\n * @group Props\n */\n @Input() dataKey: string | undefined;\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 | undefined;\n /**\n * Transition options of the show animation.\n * @group Props\n */\n @Input() showTransitionOptions: string = '.12s cubic-bezier(0, 0, 0.2, 1)';\n /**\n * Transition options of the hide animation.\n * @group Props\n */\n @Input() hideTransitionOptions: string = '.1s linear';\n /**\n * When present, it specifies that the component should automatically get focus on load.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autofocus: boolean | undefined;\n /**\n * Used to define a string that autocomplete attribute the current element.\n * @group Props\n */\n @Input() autocomplete: string = 'off';\n /**\n * Name of the options field of an option group.\n * @group Props\n */\n @Input() optionGroupChildren: string | undefined = 'items';\n /**\n * Name of the label field of an option group.\n * @group Props\n */\n @Input() optionGroupLabel: string | undefined = 'label';\n /**\n * Options for the overlay element.\n * @group Props\n */\n @Input() overlayOptions: OverlayOptions | undefined;\n /**\n * An array of suggestions to display.\n * @group Props\n */\n @Input() get suggestions(): any[] {\n return this._suggestions();\n }\n set suggestions(value: any[]) {\n this._suggestions.set(value);\n this.handleSuggestionsChange();\n }\n /**\n * Element dimensions of option for virtual scrolling.\n * @group Props\n * @deprecated use virtualScrollItemSize property instead.\n */\n @Input() get itemSize(): number {\n return this._itemSize as number;\n }\n set itemSize(val: number) {\n this._itemSize = val;\n console.warn('The itemSize property is deprecated, use virtualScrollItemSize property instead.');\n }\n /**\n * Property name or getter function to use as the label of an option.\n * @group Props\n */\n @Input() optionLabel: string | ((item: any) => string) | undefined;\n /**\n * Property name or getter function to use as the value of an option.\n * @group Props\n */\n @Input() optionValue: string | ((item: any) => string) | undefined;\n /**\n * Unique identifier of the component.\n * @group Props\n */\n @Input() id: string | undefined;\n /**\n * Text to display when the search is active. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue '{0} results are available'\n */\n @Input() searchMessage: string | undefined;\n /**\n * Text to display when filtering does not return any results. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue 'No selected item'\n */\n @Input() emptySelectionMessage: string | undefined;\n /**\n * Text to be displayed in hidden accessible field when options are selected. Defaults to global value in i18n translation configuration.\n * @group Props\n * @defaultValue '{0} items selected'\n */\n @Input() selectionMessage: string | undefined;\n /**\n * Whether to focus on the first visible or selected element when the overlay panel is shown.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) autoOptionFocus: boolean | undefined = false;\n /**\n * When enabled, the focused option is selected.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) selectOnFocus: boolean | undefined;\n /**\n * Locale to use in searching. The default locale is the host environment's current locale.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) searchLocale: boolean | undefined;\n /**\n * Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.\n * @group Props\n */\n @Input() optionDisabled: string | undefined;\n /**\n * When enabled, the hovered option will be focused.\n * @group Props\n */\n @Input({ transform: booleanAttribute }) focusOnHover: boolean | undefined;\n /**\n * Specifies the input variant of the component.\n * @group Props\n */\n @Input() variant: 'filled' | 'outlined' = 'outlined';\n /**\n * Callback to invoke to search for suggestions.\n * @param {AutoCompleteCompleteEvent} event - Custom complete event.\n * @group Emits\n */\n @Output() completeMethod: EventEmitter<AutoCompleteCompleteEvent> = new EventEmitter<AutoCompleteCompleteEvent>();\n /**\n * Callback to invoke when a suggestion is selected.\n * @param {AutoCompleteSelectEvent} event - custom select event.\n * @group Emits\n */\n @Output() onSelect: EventEmitter<AutoCompleteSelectEvent> = new EventEmitter<AutoCompleteSelectEvent>();\n /**\n * Callback to invoke when a selected value is removed.\n * @param {AutoCompleteUnselectEvent} event - custom unselect event.\n * @group Emits\n */\n @Output() onUnselect: EventEmitter<AutoCompleteUnselectEvent> = new EventEmitter<AutoCompleteUnselectEvent>();\n /**\n * Callback to invoke when the component receives focus.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onFocus: EventEmitter<Event> = new EventEmitter();\n /**\n * Callback to invoke when the component loses focus.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onBlur: EventEmitter<Event> = new EventEmitter();\n /**\n * Callback to invoke to when dropdown button is clicked.\n * @param {AutoCompleteDropdownClickEvent} event - custom dropdown click event.\n * @group Emits\n */\n @Output() onDropdownClick: EventEmitter<AutoCompleteDropdownClickEvent> = new EventEmitter<AutoCompleteDropdownClickEvent>();\n /**\n * Callback to invoke when clear button is clicked.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onClear: EventEmitter<Event | undefined> = new EventEmitter<Event | undefined>();\n /**\n * Callback to invoke on input key up.\n * @param {KeyboardEvent} event - Keyboard event.\n * @group Emits\n */\n @Output() onKeyUp: EventEmitter<KeyboardEvent> = new EventEmitter();\n /**\n * Callback to invoke on overlay is shown.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onShow: EventEmitter<Event> = new EventEmitter<Event>();\n /**\n * Callback to invoke on overlay is hidden.\n * @param {Event} event - Browser event.\n * @group Emits\n */\n @Output() onHide: EventEmitter<Event> = new EventEmitter<Event>();\n /**\n * Callback to invoke on lazy load data.\n * @param {AutoCompleteLazyLoadEvent} event - Lazy load event.\n * @group Emits\n */\n @Output() onLazyLoad: EventEmitter<AutoCompleteLazyLoadEvent> = new EventEmitter<AutoCompleteLazyLoadEvent>();\n\n @ViewChild('container') containerEL: Nullable<ElementRef>;\n\n @ViewChild('focusInput') inputEL: Nullable<ElementRef>;\n\n @ViewChild('multiIn') multiInputEl: Nullable<ElementRef>;\n\n @ViewChild('multiContainer') multiContainerEL: Nullable<ElementRef>;\n\n @ViewChild('ddBtn') dropdownButton: Nullable<ElementRef>;\n\n @ViewChild('items') itemsViewChild: Nullable<ElementRef>;\n\n @ViewChild('scroller') scroller: Nullable<Scroller>;\n\n @ViewChild('overlay') overlayViewChild!: Overlay;\n\n @ContentChildren(PrimeTemplate) templates: Nullable<QueryList<PrimeTemplate>>;\n\n _itemSize: Nullable<number>;\n\n itemsWrapper: Nullable<HTMLDivElement>;\n\n itemTemplate: Nullable<TemplateRef<any>>;\n\n emptyTemplate: Nullable<TemplateRef<any>>;\n\n headerTemplate: Nullable<TemplateRef<any>>;\n\n footerTemplate: Nullable<TemplateRef<any>>;\n\n selectedItemTemplate: Nullable<TemplateRef<any>>;\n\n groupTemplate: Nullable<TemplateRef<any>>;\n\n loaderTemplate: Nullable<TemplateRef<any>>;\n\n removeIconTemplate: Nullable<TemplateRef<any>>;\n\n loadingIconTemplate: Nullable<TemplateRef<any>>;\n\n clearIconTemplate: Nullable<TemplateRef<any>>;\n\n dropdownIconTemplate: Nullable<TemplateRef<any>>;\n\n value: string | any;\n\n _suggestions = signal<any>(null);\n\n onModelChange: Function = () => {};\n\n onModelTouched: Function = () => {};\n\n timeout: Nullable<any>;\n\n overlayVisible: boolean | undefined;\n\n suggestionsUpdated: Nullable<boolean>;\n\n highlightOption: any;\n\n highlightOptionChanged: Nullable<boolean>;\n\n focused: boolean = false;\n\n _filled: boolean;\n\n get filled() {\n return this._filled;\n }\n set filled(value: any) {\n this._filled = value;\n }\n\n loading: Nullable<boolean>;\n\n scrollHandler: Nullable<ConnectedOverlayScrollHandler>;\n\n listId: string | undefined;\n\n searchTimeout: any;\n\n dirty: boolean = false;\n\n modelValue = signal<any>(null);\n\n focusedMultipleOptionIndex = signal<number>(-1);\n\n focusedOptionIndex = signal<number>(-1);\n\n visibleOptions = computed(() => {\n return this.group ? this.flatOptions(this._suggestions()) : this._suggestions() || [];\n });\n\n inputValue = computed(() => {\n const modelValue = this.modelValue();\n const selectedOption = this.optionValueSelected ? (this.suggestions || []).find((item: any) => ObjectUtils.resolveFieldData(item, this.optionValue) === modelValue) : modelValue;\n\n if (modelValue) {\n if (typeof modelValue === 'object' || this.optionValueSelected) {\n const label = this.getOptionLabel(selectedOption);\n\n return label != null ? label : modelValue;\n } else {\n return modelValue;\n }\n } else {\n return '';\n }\n });\n\n get focusedMultipleOptionId() {\n return this.focusedMultipleOptionIndex() !== -1 ? `${this.id}_multiple_option_${this.focusedMultipleOptionIndex()}` : null;\n }\n\n get focusedOptionId() {\n return this.focusedOptionIndex() !== -1 ? `${this.id}_${this.focusedOptionIndex()}` : null;\n }\n\n get containerClass() {\n return {\n 'p-autocomplete p-component p-inputwrapper': true,\n 'p-disabled': this.disabled,\n 'p-focus': this.focused,\n 'p-autocomplete-dd': this.dropdown,\n 'p-autocomplete-multiple': this.multiple,\n 'p-inputwrapper-focus': this.focused,\n 'p-overlay-open': this.overlayVisible\n };\n }\n\n get multiContainerClass() {\n return { 'p-autocomplete-multiple-container p-component p-inputtext': true, 'p-variant-filled': this.variant === 'filled' || this.config.inputStyle() === 'filled' };\n }\n\n get panelClass() {\n return {\n 'p-autocomplete-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 inputClass() {\n return {\n 'p-autocomplete-input p-inputtext p-component': !this.multiple,\n 'p-autocomplete-dd-input': this.dropdown,\n 'p-variant-filled': this.variant === 'filled' || this.config.inputStyle() === 'filled'\n };\n }\n\n get searchResultMessageText() {\n return ObjectUtils.isNotEmpty(this.visibleOptions()) && this.overlayVisible ? this.searchMessageText.replaceAll('{0}', this.visibleOptions().length) : this.emptySearchMessageText;\n }\n\n get searchMessageText() {\n return this.searchMessage || this.config.translation.searchMessage || '';\n }\n\n get emptySearchMessageText() {\n return this.emptyMessage || this.config.translation.emptySearchMessage || '';\n }\n\n get selectionMessageText() {\n return this.selectionMessage || this.config.translation.selectionMessage || '';\n }\n\n get emptySelectionMessageText() {\n return this.emptySelectionMessage || this.config.translation.emptySelectionMessage || '';\n }\n\n get selectedMessageText() {\n return this.hasSelectedOption() ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.modelValue().length : '1') : this.emptySelectionMessageText;\n }\n\n get ariaSetSize() {\n return this.visibleOptions().filter((option) => !this.isOptionGroup(option)).length;\n }\n\n get listLabel(): string {\n return this.config.getTranslation(TranslationKeys.ARIA)['listLabel'];\n }\n\n get virtualScrollerDisabled() {\n return !this.virtualScroll;\n }\n\n get optionValueSelected() {\n return typeof this.modelValue() === 'string' && this.optionValue;\n }\n\n constructor(\n @Inject(DOCUMENT) private document: Document,\n public el: ElementRef,\n public renderer: Renderer2,\n public cd: ChangeDetectorRef,\n public config: PrimeNGConfig,\n public overlayService: OverlayService,\n private zone: NgZone\n ) {\n effect(() => {\n this.filled = ObjectUtils.isNotEmpty(this.modelValue());\n });\n }\n\n ngOnInit() {\n this.id = this.id || UniqueComponentId();\n this.cd.detectChanges();\n }\n\n ngAfterViewChecked() {\n //Use timeouts as since Angular 4.2, AfterViewChecked is broken and not called after panel is updated\n if (this.suggestionsUpdated && this.overlayViewChild) {\n this.zone.runOutsideAngular(() => {\n setTimeout(() => {\n if (this.overlayViewChild) {\n this.overlayViewChild.alignOverlay();\n }\n }, 1);\n this.suggestionsUpdated = false;\n });\n }\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 'selectedItem':\n this.selectedItemTemplate = item.template;\n break;\n\n case 'header':\n this.headerTemplate = 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 break;\n\n case 'removetokenicon':\n this.removeIconTemplate = item.template;\n break;\n\n case 'loadingicon':\n this.loadingIconTemplate = item.template;\n break;\n\n case 'clearicon':\n this.clearIconTemplate = item.template;\n break;\n\n case 'dropdownicon':\n this.dropdownIconTemplate = item.template;\n break;\n\n default:\n this.itemTemplate = item.template;\n break;\n }\n });\n }\n\n handleSuggestionsChange() {\n if (this.loading) {\n this._suggestions().length > 0 || this.showEmptyMessage ? this.show() : !!this.emptyTemplate ? this.show() : this.hide();\n const focusedOptionIndex = this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;\n this.focusedOptionIndex.set(focusedOptionIndex);\n this.suggestionsUpdated = true;\n this.loading = false;\n this.cd.markForCheck();\n }\n }\n\n flatOptions(options) {\n return (options || []).reduce((result, option, index) => {\n result.push({ optionGroup: option, group: true, index });\n\n const optionGroupChildren = this.getOptionGroupChildren(option);\n\n optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));\n\n return result;\n }, []);\n }\n\n isOptionGroup(option) {\n return this.optionGroupLabel && option.optionGroup && option.group;\n }\n\n findFirstOptionIndex() {\n return this.visibleOptions().findIndex((option) => this.isValidOption(option));\n }\n\n findLastOptionIndex() {\n return ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option));\n }\n\n findFirstFocusedOptionIndex() {\n const selectedIndex = this.findSelectedOptionIndex();\n\n return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;\n }\n\n findLastFocusedOptionIndex() {\n const selectedIndex = this.findSelectedOptionIndex();\n\n return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;\n }\n\n findSelectedOptionIndex() {\n return this.hasSelectedOption() ? this.visibleOptions().findIndex((option) => this.isValidSelectedOption(option)) : -1;\n }\n\n findNextOptionIndex(index) {\n const matchedOptionIndex =\n index < this.visibleOptions().length - 1\n ? this.visibleOptions()\n .slice(index + 1)\n .findIndex((option) => this.isValidOption(option))\n : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;\n }\n\n findPrevOptionIndex(index) {\n const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1;\n\n return matchedOptionIndex > -1 ? matchedOptionIndex : index;\n }\n\n isValidSelectedOption(option) {\n return this.isValidOption(option) && this.isSelected(option);\n }\n\n isValidOption(option) {\n return option && !(this.isOptionDisabled(option) || this.isOptionGroup(option));\n }\n\n isOptionDisabled(option) {\n return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;\n }\n\n isSelected(option) {\n if (this.multiple) {\n return this.unique ? this.modelValue()?.find((model) => ObjectUtils.equals(model, this.getOptionValue(option), this.equalityKey())) : false;\n }\n return ObjectUtils.equals(this.modelValue(), this.getOptionValue(option), this.equalityKey());\n }\n\n isOptionMatched(option, value) {\n return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.searchLocale) === value.toLocaleLowerCase(this.searchLocale);\n }\n\n isInputClicked(event) {\n return event.target === this.inputEL.nativeElement;\n }\n isDropdownClicked(event) {\n return this.dropdownButton?.nativeElement ? event.target === this.dropdownButton.nativeElement || this.dropdownButton.nativeElement.contains(event.target) : false;\n }\n equalityKey() {\n return this.dataKey; // TODO: The 'optionValue' properties can be added.\n }\n\n onContainerClick(event) {\n if (this.disabled || this.loading || this.isInputClicked(event) || this.isDropdownClicked(event)) {\n return;\n }\n\n if (!this.overlayViewChild || !this.overlayViewChild.overlayViewChild?.nativeElement.contains(event.target)) {\n DomHandler.focus(this.inputEL.nativeElement);\n }\n }\n\n handleDropdownClick(event) {\n let query = undefined;\n\n if (this.overlayVisible) {\n this.hide(true);\n } else {\n DomHandler.focus(this.inputEL.nativeElement);\n query = this.inputEL.nativeElement.value;\n\n if (this.dropdownMode === 'blank') this.search(event, '', 'dropdown');\n else if (this.dropdownMode === 'current') this.search(event, query, 'dropdown');\n }\n\n this.onDropdownClick.emit({ originalEvent: event, query });\n }\n\n onInput(event) {\n if (this.searchTimeout) {\n clearTimeout(this.searchTimeout);\n }\n\n let query = event.target.value;\n if (this.maxlength !== null) {\n query = query.split('').slice(0, this.maxlength).join('');\n }\n\n if (!this.multiple && !this.forceSelection) {\n this.updateModel(query);\n }\n\n if (query.length === 0 && !this.multiple) {\n this.onClear.emit();\n\n setTimeout(() => {\n this.hide();\n }, this.delay / 2);\n } else {\n if (query.length >= this.minLength) {\n this.focusedOptionIndex.set(-1);\n\n this.searchTimeout = setTimeout(() => {\n this.search(event, query, 'input');\n }, this.delay);\n } else {\n this.hide();\n }\n }\n }\n\n onInputChange(event) {\n if (this.forceSelection) {\n let valid = false;\n\n if (this.visibleOptions()) {\n const matchedValue = this.visibleOptions().find((option) => this.isOptionMatched(option, this.inputEL.nativeElement.value || ''));\n\n if (matchedValue !== undefined) {\n valid = true;\n !this.isSelected(matchedValue) && this.onOptionSelect(event, matchedValue);\n }\n }\n\n if (!valid) {\n this.inputEL.nativeElement.value = '';\n !this.multiple && this.updateModel(null);\n }\n }\n }\n\n onInputFocus(event) {\n if (this.disabled) {\n // For ScreenReaders\n return;\n }\n\n if (!this.dirty && this.completeOnFocus) {\n this.search(event, event.target.value, 'focus');\n }\n this.dirty = true;\n this.focused = true;\n const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;\n this.focusedOptionIndex.set(focusedOptionIndex);\n this.overlayVisible && this.scrollInView(this.focusedOptionIndex());\n this.onFocus.emit(event);\n }\n\n onMultipleContainerFocus(event) {\n if (this.disabled) {\n // For ScreenReaders\n return;\n }\n\n this.focused = true;\n }\n\n onMultipleContainerBlur(event) {\n this.focusedMultipleOptionIndex.set(-1);\n this.focused = false;\n }\n\n onMultipleContainerKeyDown(event) {\n if (this.disabled) {\n event.preventDefault();\n\n return;\n }\n\n switch (event.code) {\n case 'ArrowLeft':\n this.onArrowLeftKeyOnMultiple(event);\n break;\n\n case 'ArrowRight':\n this.onArrowRightKeyOnMultiple(event);\n break;\n\n case 'Backspace':\n this.onBackspaceKeyOnMultiple(event);\n break;\n\n default:\n break;\n }\n }\n\n onInputBlur(event) {\n this.dirty = false;\n this.focused = false;\n this.focusedOptionIndex.set(-1);\n this.onModelTouched();\n this.onBlur.emit(event);\n }\n\n onInputPaste(event) {\n this.onKeyDown(event);\n }\n\n onInputKeyUp(event) {\n this.onKeyUp.emit(event);\n }\n\n onKeyDown(event) {\n if (this.disabled) {\n event.preventDefault();\n\n return;\n }\n\n switch (event.code) {\n case 'ArrowDown':\n this.onArrowDownKey(event);\n break;\n\n case 'ArrowUp':\n this.onArrowUpKey(event);\n break;\n\n case 'ArrowLeft':\n this.onArrowLeftKey(event);\n break;\n\n case 'ArrowRight':\n this.onArrowRightKey(event);\n break;\n\n case 'Home':\n this.onHomeKey(event);\n break;\n\n case 'End':\n this.onEndKey(event);\n break;\n\n case 'PageDown':\n this.onPageDownKey(event);\n break;\n\n case 'PageUp':\n this.onPageUpKey(event);\n break;\n\n case 'Enter':\n case 'NumpadEnter':\n this.onEnterKey(event);\n break;\n\n case 'Escape':\n this.onEscapeKey(event);\n break;\n\n case 'Tab':\n this.onTabKey(event);\n break;\n\n case 'Backspace':\n this.onBackspaceKey(event);\n break;\n\n case 'ShiftLeft':\n case 'ShiftRight':\n //NOOP\n break;\n\n default:\n break;\n }\n }\n\n onArrowDownKey(event) {\n if (!this.overlayVisible) {\n return;\n }\n\n const optionIndex = this.focusedOptionIndex() !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex()) : this.findFirstFocusedOptionIndex();\n\n this.changeFocusedOptionIndex(event, optionIndex);\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n onArrowUpKey(event) {\n if (!this.overlayVisible) {\n return;\n }\n\n