@millieofzo/ion-selectable
Version:
Ionic selectable with extended features
1 lines • 124 kB
Source Map (JSON)
{"version":3,"file":"millieofzo-ion-selectable.mjs","sources":["../../../../libs/ion-selectable/src/lib/ion-selectable/ion-selectable-modal.component.ts","../../../../libs/ion-selectable/src/lib/ion-selectable/ion-selectable-modal.component.html","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-add-item-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-close-button-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-footer-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-group-end-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-group-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-header-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-icon-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-item-end-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-item-icon-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-item-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-message-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-placeholder-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-search-fail-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-title-template.directive.ts","../../../../libs/ion-selectable/src/lib/directives/ionic-selectable-value-template.directive.ts","../../../../libs/ion-selectable/src/lib/ion-selectable/ion-selectable.component.ts","../../../../libs/ion-selectable/src/lib/ion-selectable/ion-selectable.component.html","../../../../libs/ion-selectable/src/millieofzo-ion-selectable.ts"],"sourcesContent":["import {\n AfterViewInit,\n Component,\n ElementRef,\n HostBinding,\n HostListener,\n ViewChild,\n} from '@angular/core';\nimport {\n IonContent,\n IonInfiniteScroll,\n IonSearchbar,\n NavParams,\n IonicModule,\n} from '@ionic/angular';\nimport { IonSelectableComponent } from './ion-selectable.component';\nimport { FormsModule } from '@angular/forms';\nimport {\n NgIf,\n NgTemplateOutlet,\n NgFor,\n NgClass,\n NgStyle,\n} from '@angular/common';\n\n@Component({\n selector: 'ion-selectable-modal',\n templateUrl: './ion-selectable-modal.component.html',\n standalone: true,\n imports: [\n IonicModule,\n NgIf,\n NgTemplateOutlet,\n FormsModule,\n NgFor,\n NgClass,\n NgStyle,\n ],\n})\nexport class IonSelectableModalComponent implements AfterViewInit {\n @ViewChild(IonContent)\n _content!: IonContent;\n _header!: HTMLElement;\n selectComponent: IonSelectableComponent;\n @ViewChild('searchbarComponent')\n _searchbarComponent!: IonSearchbar;\n @ViewChild(IonInfiniteScroll)\n _infiniteScroll!: IonInfiniteScroll;\n @HostBinding('class.ionic-selectable-modal')\n _cssClass = true;\n @HostBinding('class.ionic-selectable-modal-can-clear')\n get _canClearCssClass(): boolean {\n return this.selectComponent.canClear;\n }\n @HostBinding('class.ionic-selectable-modal-is-multiple')\n get _isMultipleCssClass(): boolean {\n return this.selectComponent.isMultiple;\n }\n @HostBinding('class.ionic-selectable-modal-is-searching')\n get _isSearchingCssClass(): boolean {\n return this.selectComponent._isSearching;\n }\n @HostBinding('class.ionic-selectable-modal-ios')\n get _isIos(): boolean {\n return this.selectComponent._isIos;\n }\n @HostBinding('class.ionic-selectable-modal-md')\n _isMD(): boolean {\n return this.selectComponent._isMD;\n }\n @HostBinding('class.ionic-selectable-modal-is-add-item-template-visible')\n get _isAddItemTemplateVisibleCssClass(): boolean {\n return this.selectComponent._isAddItemTemplateVisible;\n }\n @HostListener('window:resize')\n onResize() {\n // ion-footer inside the template might change its height when\n // device orientation changes.\n this.selectComponent._positionAddItemTemplate();\n }\n\n constructor(private navParams: NavParams, public _element: ElementRef) {\n this.selectComponent = this.navParams.get('selectComponent');\n this.selectComponent._modalComponent = this;\n this.selectComponent._selectedItems = [];\n\n if (!this.selectComponent._isNullOrWhiteSpace(this.selectComponent.value)) {\n if (this.selectComponent.isMultiple) {\n this.selectComponent.value.forEach((item: any) => {\n this.selectComponent._selectedItems.push(item);\n });\n } else {\n this.selectComponent._selectedItems.push(this.selectComponent.value);\n }\n }\n\n this.selectComponent._setItemsToConfirm(\n this.selectComponent._selectedItems\n );\n }\n\n ngAfterViewInit() {\n this._header = this._element.nativeElement.querySelector('ion-header');\n\n if (this._searchbarComponent && this.selectComponent.shouldFocusSearchbar) {\n // Focus after a delay because focus doesn't work without it.\n setTimeout(() => {\n this._searchbarComponent.setFocus();\n }, 1000);\n }\n }\n}\n","<ion-header>\n <ion-toolbar *ngIf=\"!selectComponent.headerTemplate\"\n [color]=\"selectComponent.headerColor ? selectComponent.headerColor : null\">\n <ion-buttons [slot]=\"selectComponent.closeButtonSlot\">\n <ion-button (click)=\"selectComponent._close()\">\n <span *ngIf=\"selectComponent.closeButtonTemplate\" [ngTemplateOutlet]=\"selectComponent.closeButtonTemplate\">\n </span>\n <span *ngIf=\"!selectComponent.closeButtonTemplate\">\n {{selectComponent.closeButtonText}}\n </span>\n </ion-button>\n </ion-buttons>\n <ion-title>\n <!-- Need span for for text ellipsis. -->\n <span *ngIf=\"selectComponent.titleTemplate\" [ngTemplateOutlet]=\"selectComponent.titleTemplate\">\n </span>\n <span *ngIf=\"!selectComponent.titleTemplate\">\n {{selectComponent.label}}\n </span>\n </ion-title>\n </ion-toolbar>\n <div *ngIf=\"selectComponent.headerTemplate\" [ngTemplateOutlet]=\"selectComponent.headerTemplate\">\n </div>\n <ion-toolbar *ngIf=\"selectComponent.canSearch || selectComponent.messageTemplate\">\n <ion-searchbar *ngIf=\"selectComponent.canSearch\" #searchbarComponent [(ngModel)]=\"selectComponent._searchText\"\n (ionInput)=\"selectComponent._filterItems()\" (ionClear)=\"selectComponent._onSearchbarClear()\"\n [placeholder]=\"selectComponent.searchPlaceholder\" [debounce]=\"selectComponent.searchDebounce\">\n </ion-searchbar>\n <div class=\"ionic-selectable-message\" *ngIf=\"selectComponent.messageTemplate\">\n <div [ngTemplateOutlet]=\"selectComponent.messageTemplate\">\n </div>\n </div>\n </ion-toolbar>\n</ion-header>\n<ion-content>\n <div class=\"ionic-selectable-spinner\" *ngIf=\"selectComponent._isSearching\">\n <div class=\"ionic-selectable-spinner-background\"></div>\n <ion-spinner></ion-spinner>\n </div>\n <ion-list class=\"ion-no-margin\" *ngIf=\"selectComponent._hasFilteredItems\">\n <ion-item-group *ngFor=\"let group of selectComponent._filteredGroups\" class=\"ionic-selectable-group\">\n <ion-item-divider *ngIf=\"selectComponent._hasGroups\"\n [color]=\"selectComponent.groupColor ? selectComponent.groupColor : null\">\n <!-- Need span for for text ellipsis. -->\n <span *ngIf=\"selectComponent.groupTemplate\" [ngTemplateOutlet]=\"selectComponent.groupTemplate\"\n [ngTemplateOutletContext]=\"{ group: group }\">\n </span>\n <!-- Need ion-label for text ellipsis. -->\n <ion-label *ngIf=\"!selectComponent.groupTemplate\">\n {{group.text}}\n </ion-label>\n <div *ngIf=\"selectComponent.groupEndTemplate\" slot=\"end\">\n <div [ngTemplateOutlet]=\"selectComponent.groupEndTemplate\" [ngTemplateOutletContext]=\"{ group: group }\">\n </div>\n </div>\n </ion-item-divider>\n <ion-item button=\"true\" detail=\"false\" *ngFor=\"let item of group.items\" (click)=\"selectComponent._select(item)\"\n class=\"ionic-selectable-item\" [ngClass]=\"{\n 'ionic-selectable-item-is-selected': selectComponent._isItemSelected(item),\n 'ionic-selectable-item-is-disabled': selectComponent._isItemDisabled(item)\n }\" [disabled]=\"selectComponent._isItemDisabled(item)\">\n <!-- Need span for text ellipsis. -->\n <span *ngIf=\"selectComponent.itemTemplate\" [ngTemplateOutlet]=\"selectComponent.itemTemplate\"\n [ngTemplateOutletContext]=\"{ item: item, isItemSelected: selectComponent._isItemSelected(item) }\">\n </span>\n <!-- Need ion-label for text ellipsis. -->\n <ion-label *ngIf=\"!selectComponent.itemTemplate\">\n {{selectComponent._formatItem(item)}}\n </ion-label>\n <div *ngIf=\"selectComponent.itemEndTemplate\" slot=\"end\">\n <div [ngTemplateOutlet]=\"selectComponent.itemEndTemplate\"\n [ngTemplateOutletContext]=\"{ item: item, isItemSelected: selectComponent._isItemSelected(item) }\">\n </div>\n </div>\n <span *ngIf=\"selectComponent.itemIconTemplate\" [ngTemplateOutlet]=\"selectComponent.itemIconTemplate\"\n [ngTemplateOutletContext]=\"{ item: item, isItemSelected: selectComponent._isItemSelected(item) }\">\n </span>\n <ion-icon *ngIf=\"!selectComponent.itemIconTemplate\"\n [name]=\"selectComponent._isItemSelected(item) ? 'checkmark-circle' : 'radio-button-off'\"\n [color]=\"selectComponent._isItemSelected(item) ? 'primary' : null\" [slot]=\"selectComponent.itemIconSlot\">\n </ion-icon>\n <ion-button *ngIf=\"selectComponent.canSaveItem\" class=\"ionic-selectable-item-button\" slot=\"end\" fill=\"outline\"\n (click)=\"selectComponent._saveItem($event, item)\">\n <ion-icon slot=\"icon-only\" ios=\"create\" md=\"create-sharp\"></ion-icon>\n </ion-button>\n <ion-button *ngIf=\"selectComponent.canDeleteItem\" class=\"ionic-selectable-item-button\" slot=\"end\" fill=\"outline\"\n (click)=\"selectComponent._deleteItemClick($event, item)\">\n <ion-icon slot=\"icon-only\" ios=\"trash\" md=\"trash-sharp\"></ion-icon>\n </ion-button>\n </ion-item>\n </ion-item-group>\n </ion-list>\n <!-- Fail text should be above InfiniteScroll to avoid a gap when no items are found. -->\n <div *ngIf=\"!selectComponent._hasFilteredItems\">\n <span *ngIf=\"selectComponent.searchFailTemplate\" [ngTemplateOutlet]=\"selectComponent.searchFailTemplate\">\n </span>\n <div *ngIf=\"!selectComponent.searchFailTemplate\" class=\"ion-margin\">\n {{selectComponent.searchFailText}}\n </div>\n </div>\n <ion-infinite-scroll [disabled]=\"!selectComponent.hasInfiniteScroll\"\n (ionInfinite)=\"selectComponent._getMoreItems()\">\n <ion-infinite-scroll-content></ion-infinite-scroll-content>\n </ion-infinite-scroll>\n</ion-content>\n<div class=\"ionic-selectable-add-item-template\" *ngIf=\"selectComponent._isAddItemTemplateVisible\"\n [ngStyle]=\"{ 'top.px': _header.offsetHeight }\">\n <div class=\"ionic-selectable-add-item-template-inner\"\n [ngStyle]=\"{ 'height': selectComponent._addItemTemplateFooterHeight }\">\n <span [ngTemplateOutlet]=\"selectComponent.addItemTemplate\"\n [ngTemplateOutletContext]=\"{ item: selectComponent._itemToAdd, isAdd: selectComponent._itemToAdd === null }\">\n </span>\n </div>\n</div>\n<ion-footer *ngIf=\"selectComponent._footerButtonsCount > 0 || selectComponent.footerTemplate\"\n [ngStyle]=\"{ 'visibility': selectComponent._isFooterVisible ? 'initial' : 'hidden' }\">\n <ion-toolbar *ngIf=\"!selectComponent.footerTemplate\">\n <ion-row>\n <ion-col *ngIf=\"selectComponent.canClear\">\n <ion-button expand=\"full\" (click)=\"selectComponent._clear()\"\n [disabled]=\"!selectComponent._selectedItems.length\">\n {{selectComponent.clearButtonText}}\n </ion-button>\n </ion-col>\n <ion-col *ngIf=\"selectComponent.canAddItem\">\n <ion-button expand=\"full\" (click)=\"selectComponent._addItemClick()\">\n {{selectComponent.addButtonText}}\n </ion-button>\n </ion-col>\n <ion-col *ngIf=\"selectComponent.isMultiple || selectComponent.hasConfirmButton\">\n <ion-button expand=\"full\" (click)=\"selectComponent._confirm()\"\n [disabled]=\"!selectComponent.isConfirmButtonEnabled\">\n {{selectComponent.confirmButtonText}}\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n <div *ngIf=\"selectComponent.footerTemplate\" [ngTemplateOutlet]=\"selectComponent.footerTemplate\">\n </div>\n</ion-footer>","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableAddItemTemplate]',\n standalone: true,\n})\nexport class IonSelectableAddItemTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableCloseButtonTemplate]',\n standalone: true,\n})\nexport class IonSelectableCloseButtonTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableFooterTemplate]',\n standalone: true,\n})\nexport class IonSelectableFooterTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableGroupEndTemplate]',\n standalone: true,\n})\nexport class IonSelectableGroupEndTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableGroupTemplate]',\n standalone: true,\n})\nexport class IonSelectableGroupTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableHeaderTemplate]',\n standalone: true,\n})\nexport class IonSelectableHeaderTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableIconTemplate]',\n standalone: true\n})\nexport class IonSelectableIconTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableItemEndTemplate]',\n standalone: true,\n})\nexport class IonSelectableItemEndTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableItemIconTemplate]',\n standalone: true\n})\nexport class IonSelectableItemIconTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableItemTemplate]',\n standalone: true\n})\nexport class IonSelectableItemTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableMessageTemplate]',\n standalone: true,\n})\nexport class IonSelectableMessageTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectablePlaceholderTemplate]',\n standalone: true,\n})\nexport class IonSelectablePlaceholderTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableSearchFailTemplate]',\n standalone: true,\n})\nexport class IonSelectableSearchFailTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableTitleTemplate]',\n standalone: true,\n})\nexport class IonSelectableTitleTemplateDirective { }\n","import { Directive } from '@angular/core';\n\n@Directive({\n selector: '[ionSelectableValueTemplate]',\n standalone: true,\n})\nexport class IonSelectableValueTemplateDirective { }\n","/* eslint-disable @typescript-eslint/no-this-alias */\n/* eslint-disable @typescript-eslint/no-empty-function */\n/* eslint-disable @angular-eslint/no-output-on-prefix */\nimport { NgFor, NgIf, NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n ContentChild,\n DoCheck,\n ElementRef,\n EventEmitter,\n HostBinding,\n Input,\n IterableDiffer,\n IterableDiffers,\n OnInit,\n Optional,\n Output,\n Renderer2,\n TemplateRef,\n ViewEncapsulation,\n forwardRef,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { IonItem, ModalController, Platform } from '@ionic/angular';\nimport { AnimationBuilder, ModalOptions } from '@ionic/core';\nimport { Subscription } from 'rxjs';\nimport { IonHeader } from '@ionic/angular/standalone';\nimport { IonSelectableModalComponent } from './ion-selectable-modal.component';\nimport {\n IonSelectableValueTemplateDirective,\n IonSelectableItemTemplateDirective,\n IonSelectableItemEndTemplateDirective,\n IonSelectableTitleTemplateDirective,\n IonSelectablePlaceholderTemplateDirective,\n IonSelectableMessageTemplateDirective,\n IonSelectableGroupTemplateDirective,\n IonSelectableGroupEndTemplateDirective,\n IonSelectableCloseButtonTemplateDirective,\n IonSelectableSearchFailTemplateDirective,\n IonSelectableAddItemTemplateDirective,\n IonSelectableFooterTemplateDirective,\n IonSelectableHeaderTemplateDirective,\n IonSelectableItemIconTemplateDirective,\n IonSelectableIconTemplateDirective,\n} from '../directives';\n\n@Component({\n selector: 'ion-selectable',\n templateUrl: './ion-selectable.component.html',\n styleUrls: ['./ion-selectable.component.scss'],\n encapsulation: ViewEncapsulation.None,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => IonSelectableComponent),\n multi: true,\n },\n ],\n standalone: true,\n imports: [IonHeader, NgIf, NgTemplateOutlet, NgFor],\n})\nexport class IonSelectableComponent\n implements ControlValueAccessor, OnInit, DoCheck\n{\n @HostBinding('class.ionic-selectable')\n _cssClass = true;\n @HostBinding('class.ionic-selectable-ios')\n _isIos!: boolean;\n @HostBinding('class.ionic-selectable-md')\n _isMD!: boolean;\n @HostBinding('class.ionic-selectable-is-multiple')\n get _isMultipleCssClass(): boolean {\n return this.isMultiple;\n }\n @HostBinding('class.ionic-selectable-has-value')\n get _hasValueCssClass(): boolean {\n return this.hasValue();\n }\n @HostBinding('class.ionic-selectable-has-placeholder')\n get _hasPlaceholderCssClass(): boolean {\n return this._hasPlaceholder;\n }\n @HostBinding('class.ionic-selectable-has-label')\n get _hasIonLabelCssClass(): boolean {\n return this._hasIonLabel;\n }\n @HostBinding('class.ionic-selectable-label-default')\n get _hasDefaultIonLabelCssClass(): boolean {\n return this._ionLabelPosition === 'default';\n }\n @HostBinding('class.ionic-selectable-label-fixed')\n get _hasFixedIonLabelCssClass(): boolean {\n return this._ionLabelPosition === 'fixed';\n }\n @HostBinding('class.ionic-selectable-label-stacked')\n get _hasStackedIonLabelCssClass(): boolean {\n return this._ionLabelPosition === 'stacked';\n }\n @HostBinding('class.ionic-selectable-label-floating')\n get _hasFloatingIonLabelCssClass(): boolean {\n return this._ionLabelPosition === 'floating';\n }\n private _isOnSearchEnabled = true;\n private _isEnabled = true;\n private _shouldBackdropClose = true;\n private _isOpened = false;\n private _value: any = null;\n private _modal!: HTMLIonModalElement;\n private _itemsDiffer: IterableDiffer<any>;\n private _hasObjects!: boolean;\n private _canClear = false;\n private _hasConfirmButton = false;\n private _isMultiple = false;\n private _canAddItem = false;\n private _addItemObservable!: Subscription;\n private _deleteItemObservable!: Subscription;\n private onItemsChange: EventEmitter<any> = new EventEmitter();\n private _ionItemElement: any;\n private _ionLabelElement: any;\n private _hasIonLabel = false;\n private _ionLabelPosition:\n | 'fixed'\n | 'stacked'\n | 'floating'\n | 'default'\n | null = null;\n private _label = '';\n private get _hasInfiniteScroll(): boolean {\n return this.isEnabled &&\n this._modalComponent &&\n this._modalComponent._infiniteScroll\n ? true\n : false;\n }\n get _shouldStoreItemValue(): boolean {\n return this.shouldStoreItemValue && this._hasObjects;\n }\n _valueItems: any[] = [];\n _searchText = '';\n _hasSearchText = false;\n _groups: any[] = [];\n _itemsToConfirm: any[] = [];\n _selectedItems: any[] = [];\n _modalComponent!: IonSelectableModalComponent;\n _filteredGroups: any[] = [];\n _hasGroups!: boolean;\n _isSearching!: boolean;\n _hasPlaceholder!: boolean;\n _isAddItemTemplateVisible = false;\n _isFooterVisible = true;\n _itemToAdd: any = null;\n _footerButtonsCount = 0;\n _hasFilteredItems = false;\n\n /**\n * Text of [Ionic Label](https://ionicframework.com/docs/api/label).\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#label).\n *\n * @readonly\n * @default null\n * @memberof IonSelectableComponent\n */\n get label(): string {\n return this._label;\n }\n\n /**\n * Text that the user has typed in Searchbar.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchtext).\n *\n * @readonly\n * @default ''\n * @memberof IonSelectableComponent\n */\n get searchText(): string {\n return this._searchText;\n }\n set searchText(searchText: string) {\n this._searchText = searchText;\n this._setHasSearchText();\n }\n\n /**\n * Determines whether search is running.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#issearching).\n *\n * @default false\n * @readonly\n * @memberof IonSelectableComponent\n */\n get isSearching(): boolean {\n return this._isSearching;\n }\n\n /**\n * Determines whether user has typed anything in Searchbar.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hassearchtext).\n *\n * @default false\n * @readonly\n * @memberof IonSelectableComponent\n */\n get hasSearchText(): boolean {\n return this._hasSearchText;\n }\n\n get value(): any {\n return this._value;\n }\n set value(value: any) {\n this._value = value;\n\n // Set value items.\n this._valueItems.splice(0, this._valueItems.length);\n\n if (this.isMultiple) {\n if (value && value.length) {\n Array.prototype.push.apply(this._valueItems, value);\n }\n } else {\n if (!this._isNullOrWhiteSpace(value)) {\n this._valueItems.push(value);\n }\n }\n\n this._setIonItemHasValue();\n this._setHasPlaceholder();\n }\n\n /**\n * A list of items.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#items).\n *\n * @default []\n * @memberof IonSelectableComponent\n */\n @Input()\n items: any[] = [];\n @Output()\n itemsChange: EventEmitter<any> = new EventEmitter();\n\n /**\n * Determines whether the component is enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isenabled).\n *\n * @default true\n * @memberof IonSelectableComponent\n */\n @HostBinding('class.ionic-selectable-is-enabled')\n @Input('isEnabled')\n get isEnabled(): boolean {\n return this._isEnabled;\n }\n set isEnabled(isEnabled: boolean) {\n this._isEnabled = !!isEnabled;\n this.enableIonItem(this._isEnabled);\n }\n\n /**\n * Determines whether Modal should be closed when backdrop is clicked.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#shouldbackdropclose).\n *\n * @default true\n * @memberof IonSelectableComponent\n */\n @Input('shouldBackdropClose')\n get shouldBackdropClose(): boolean {\n return this._shouldBackdropClose;\n }\n set shouldBackdropClose(shouldBackdropClose: boolean) {\n this._shouldBackdropClose = !!shouldBackdropClose;\n }\n\n /**\n * Modal CSS class.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalcssclass).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n modalCssClass = '';\n\n /**\n * Modal enter animation.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalenteranimation).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n modalEnterAnimation!: AnimationBuilder;\n\n /**\n * Modal leave animation.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#modalleaveanimation).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n modalLeaveAnimation!: AnimationBuilder;\n\n /**\n * Determines whether Modal is opened.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isopened).\n *\n * @default false\n * @readonly\n * @memberof IonSelectableComponent\n */\n get isOpened(): boolean {\n return this._isOpened;\n }\n\n /**\n * Determines whether Confirm button is enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isconfirmbuttonenabled).\n *\n * @default true\n * @memberof IonSelectableComponent\n */\n @Input()\n isConfirmButtonEnabled = true;\n\n /**\n * Determines whether Confirm button is visible for single selection.\n * By default Confirm button is visible only for multiple selection.\n * **Note**: It is always true for multiple selection and cannot be changed.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasconfirmbutton).\n *\n * @default true\n * @memberof IonSelectableComponent\n */\n @Input('hasConfirmButton')\n get hasConfirmButton(): boolean {\n return this._hasConfirmButton;\n }\n set hasConfirmButton(hasConfirmButton: boolean) {\n this._hasConfirmButton = !!hasConfirmButton;\n this._countFooterButtons();\n }\n\n /**\n * Item property to use as a unique identifier, e.g, `'id'`.\n * **Note**: `items` should be an object array.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemvaluefield).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n itemValueField = '';\n\n /**\n * Item property to display, e.g, `'name'`.\n * **Note**: `items` should be an object array.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemtextfield).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n itemTextField = '';\n\n /**\n *\n * Group property to use as a unique identifier to group items, e.g. `'country.id'`.\n * **Note**: `items` should be an object array.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#groupvaluefield).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n groupValueField = '';\n\n /**\n * Group property to display, e.g. `'country.name'`.\n * **Note**: `items` should be an object array.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#grouptextfield).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n groupTextField = '';\n\n /**\n * Determines whether to show Searchbar.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#cansearch).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n canSearch = false;\n\n /**\n * Determines whether `onSearch` event is enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#isonsearchenabled).\n *\n * @default true\n * @memberof IonSelectableComponent\n */\n @Input('isOnSearchEnabled')\n get isOnSearchEnabled(): boolean {\n return this._isOnSearchEnabled;\n }\n set isOnSearchEnabled(isOnSearchEnabled: boolean) {\n this._isOnSearchEnabled = !!isOnSearchEnabled;\n }\n\n /**\n * Determines whether to show Clear button.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#canclear).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @HostBinding('class.ionic-selectable-can-clear')\n @Input('canClear')\n get canClear(): boolean {\n return this._canClear;\n }\n set canClear(canClear: boolean) {\n this._canClear = !!canClear;\n this._countFooterButtons();\n }\n\n /**\n * Determines whether Ionic [InfiniteScroll](https://ionicframework.com/docs/api/components/infinite-scroll/InfiniteScroll/) is enabled.\n * **Note**: Infinite scroll cannot be used together with virtual scroll.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasinfinitescroll).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n hasInfiniteScroll = false;\n\n /**\n * Determines whether Ionic [VirtualScroll](https://ionicframework.com/docs/api/components/virtual-scroll/VirtualScroll/) is enabled.\n * **Note**: Virtual scroll cannot be used together with infinite scroll.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#hasvirtualscroll).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n hasVirtualScroll = false;\n\n /**\n * See Ionic VirtualScroll [approxItemHeight](https://ionicframework.com/docs/api/components/virtual-scroll/VirtualScroll/).\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#virtualscrollapproxitemheight).\n *\n * @default '40px'\n * @memberof IonSelectableComponent\n */\n @Input()\n virtualScrollApproxItemHeight = '40px';\n\n /**\n * A placeholder for Searchbar.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchplaceholder).\n *\n * @default 'Search'\n * @memberof IonSelectableComponent\n */\n @Input()\n searchPlaceholder = 'Search';\n\n /**\n * A placeholder.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#placeholder).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n placeholder = '';\n\n /**\n * Determines whether multiple items can be selected.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#ismultiple).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input('isMultiple')\n get isMultiple(): boolean {\n return this._isMultiple;\n }\n set isMultiple(isMultiple: boolean) {\n this._isMultiple = !!isMultiple;\n this._countFooterButtons();\n }\n\n /**\n * Text to display when no items have been found during search.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchfailtext).\n *\n * @default 'No items found.'\n * @memberof IonSelectableComponent\n */\n @Input()\n searchFailText = 'No items found.';\n\n /**\n * Clear button text.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#clearbuttontext).\n *\n * @default 'Clear'\n * @memberof IonSelectableComponent\n */\n @Input()\n clearButtonText = 'Clear';\n\n /**\n * Add button text.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#addbuttontext).\n *\n * @default 'Add'\n * @memberof IonSelectableComponent\n */\n @Input()\n addButtonText = 'Add';\n\n /**\n * Confirm button text.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#confirmbuttontext).\n *\n * @default 'OK'\n * @memberof IonSelectableComponent\n */\n @Input()\n confirmButtonText = 'OK';\n\n /**\n * Close button text.\n * The field is only applicable to **iOS** platform, on **Android** only Cross icon is displayed.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#closebuttontext).\n *\n * @default 'Cancel'\n * @memberof IonSelectableComponent\n */\n @Input()\n closeButtonText = 'Cancel';\n\n /**\n * Determines whether Searchbar should receive focus when Modal is opened.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#shouldfocussearchbar).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n shouldFocusSearchbar = false;\n\n /**\n * Header color. [Ionic colors](https://ionicframework.com/docs/theming/advanced#colors) are supported.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#headercolor).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n headerColor = '';\n\n /**\n * Group color. [Ionic colors](https://ionicframework.com/docs/theming/advanced#colors) are supported.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#groupcolor).\n *\n * @default null\n * @memberof IonSelectableComponent\n */\n @Input()\n groupColor = '';\n\n /**\n * Close button slot. [Ionic slots](https://ionicframework.com/docs/api/buttons) are supported.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#closebuttonslot).\n *\n * @default 'start'\n * @memberof IonSelectableComponent\n */\n @Input()\n closeButtonSlot = 'start';\n\n /**\n * Item icon slot. [Ionic slots](https://ionicframework.com/docs/api/item) are supported.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemiconslot).\n *\n * @default 'start'\n * @memberof IonSelectableComponent\n */\n @Input()\n itemIconSlot = 'start';\n\n /**\n * Fires when item/s has been selected and Modal closed.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onchange).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onChange: EventEmitter<{ component: IonSelectableComponent; value: any }> =\n new EventEmitter();\n\n /**\n * Fires when the user is typing in Searchbar.\n * **Note**: `canSearch` and `isOnSearchEnabled` has to be enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onsearch).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onSearch: EventEmitter<{\n component: IonSelectableComponent;\n text: string;\n }> = new EventEmitter();\n\n /**\n * Fires when no items have been found.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onsearchfail).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onSearchFail: EventEmitter<{\n component: IonSelectableComponent;\n text: string;\n }> = new EventEmitter();\n\n /**\n * Fires when some items have been found.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onsearchsuccess).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onSearchSuccess: EventEmitter<{\n component: IonSelectableComponent;\n text: string;\n }> = new EventEmitter();\n\n /**\n * Fires when the user has scrolled to the end of the list.\n * **Note**: `hasInfiniteScroll` has to be enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#oninfinitescroll).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onInfiniteScroll: EventEmitter<{\n component: IonSelectableComponent;\n text: string;\n }> = new EventEmitter();\n\n /**\n * Fires when Modal has been opened.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onopen).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onOpen: EventEmitter<{ component: IonSelectableComponent }> =\n new EventEmitter();\n\n /**\n * Fires when Modal has been closed.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onclose).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onClose: EventEmitter<{ component: IonSelectableComponent }> =\n new EventEmitter();\n\n /**\n * Fires when an item has been selected or unselected.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onselect).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onSelect: EventEmitter<{\n component: IonSelectableComponent;\n item: any;\n isSelected: boolean;\n }> = new EventEmitter();\n\n /**\n * Fires when Clear button has been clicked.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onclear).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onClear: EventEmitter<{ component: IonSelectableComponent; items: any[] }> =\n new EventEmitter();\n\n /**\n * A list of items that are selected and awaiting confirmation by user, when he has clicked Confirm button.\n * After the user has clicked Confirm button items to confirm are cleared.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#itemstoconfirm).\n *\n * @default []\n * @readonly\n * @memberof IonSelectableComponent\n */\n get itemsToConfirm(): any[] {\n return this._itemsToConfirm;\n }\n\n /**\n * How long, in milliseconds, to wait to filter items or to trigger `onSearch` event after each keystroke.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#searchdebounce).\n *\n * @default 250\n * @memberof IonSelectableComponent\n */\n @Input()\n searchDebounce = 250;\n\n /**\n * A list of items to disable.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#disableditems).\n *\n * @default []\n * @memberof IonSelectableComponent\n */\n @Input()\n disabledItems: any[] = [];\n\n /**\n * Determines whether item value only should be stored in `ngModel`, not the entire item.\n * **Note**: Item value is defined by `itemValueField`.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#shouldstoreitemvalue).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n shouldStoreItemValue = false;\n\n /**\n * Determines whether to allow editing items.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#cansaveitem).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n canSaveItem = false;\n\n /**\n * Determines whether to allow deleting items.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#candeleteitem).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input()\n canDeleteItem = false;\n\n /**\n * Determines whether to allow adding items.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#canadditem).\n *\n * @default false\n * @memberof IonSelectableComponent\n */\n @Input('canAddItem')\n get canAddItem(): boolean {\n return this._canAddItem;\n }\n set canAddItem(canAddItem: boolean) {\n this._canAddItem = !!canAddItem;\n this._countFooterButtons();\n }\n\n /**\n * Fires when Edit item button has been clicked.\n * When the button has been clicked `IonSelectableAddItemTemplate` will be shown. Use the template to create a form to edit item.\n * **Note**: `canSaveItem` has to be enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onsaveitem).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onSaveItem: EventEmitter<{ component: IonSelectableComponent; item: any }> =\n new EventEmitter();\n\n /**\n * Fires when Delete item button has been clicked.\n * **Note**: `canDeleteItem` has to be enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#ondeleteitem).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onDeleteItem: EventEmitter<{\n component: IonSelectableComponent;\n item: any;\n }> = new EventEmitter();\n\n /**\n * Fires when Add item button has been clicked.\n * When the button has been clicked `IonSelectableAddItemTemplate` will be shown. Use the template to create a form to add item.\n * **Note**: `canAddItem` has to be enabled.\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#onadditem).\n *\n * @memberof IonSelectableComponent\n */\n @Output()\n onAddItem: EventEmitter<{ component: IonSelectableComponent }> =\n new EventEmitter();\n\n @ContentChild(IonSelectableValueTemplateDirective, { read: TemplateRef })\n valueTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableItemTemplateDirective, { read: TemplateRef })\n itemTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableItemEndTemplateDirective, { read: TemplateRef })\n itemEndTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableTitleTemplateDirective, { read: TemplateRef })\n titleTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectablePlaceholderTemplateDirective, {\n read: TemplateRef,\n })\n placeholderTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableMessageTemplateDirective, { read: TemplateRef })\n messageTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableGroupTemplateDirective, { read: TemplateRef })\n groupTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableGroupEndTemplateDirective, { read: TemplateRef })\n groupEndTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableCloseButtonTemplateDirective, {\n read: TemplateRef,\n })\n closeButtonTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableSearchFailTemplateDirective, {\n read: TemplateRef,\n })\n searchFailTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableAddItemTemplateDirective, { read: TemplateRef })\n addItemTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableFooterTemplateDirective, { read: TemplateRef })\n footerTemplate!: TemplateRef<any>;\n _addItemTemplateFooterHeight!: string;\n @ContentChild(IonSelectableHeaderTemplateDirective, { read: TemplateRef })\n headerTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableItemIconTemplateDirective, { read: TemplateRef })\n itemIconTemplate!: TemplateRef<any>;\n @ContentChild(IonSelectableIconTemplateDirective, { read: TemplateRef })\n iconTemplate!: TemplateRef<any>;\n\n /**\n * See Ionic VirtualScroll [headerFn](https://ionicframework.com/docs/api/components/virtual-scroll/VirtualScroll/).\n * See more on [GitHub](https://github.com/eakoriakin/ionic-selectable/wiki/Documentation#virtualscrollheaderfn).\n *\n * @memberof IonSelectableComponent\n */\n @Input()\n virtualScrollHeaderFn = () => {\n return null;\n };\n\n constructor(\n private _modalController: ModalController,\n private _platform: Platform,\n @Optional() private ionItem: IonItem,\n private _iterableDiffers: IterableDiffers,\n private _element: ElementRef,\n private _renderer: Renderer2\n ) {\n if (!this.items?.length) {\n this.items = [];\n }\n\n this._itemsDiffer = this._iterableDiffers.find(this.items).create();\n }\n\n initFocus() {}\n\n enableIonItem(isEnabled: boolean) {\n if (!this.ionItem) {\n return;\n }\n\n this.ionItem.disabled = !isEnabled;\n }\n\n _isNullOrWhiteSpace(value: any): boolean {\n if (value === null || value === undefined) {\n return true;\n }\n\n // Convert value to string in case if it's not.\n return value.toString().replace(/\\s/g, '').length < 1;\n }\n\n _setHasSearchText() {\n this._hasSearchText = !this._isNullOrWhiteSpace(this._searchText);\n }\n\n _hasOnSearch(): boolean {\n return this.isOnSearchEnabled && this.onSearch.observers.length > 0;\n }\n\n _hasOnSaveItem(): boolean {\n return this.canSaveItem && this.onSaveItem.observers.length > 0;\n }\n\n _hasOnAddItem(): boolean {\n return this.canAddItem && this.onAddItem.observers.length > 0;\n }\n\n _hasOnDeleteItem(): boolean {\n return this.canDeleteItem && this.onDeleteItem.observers.length > 0;\n }\n\n _emitValueChange() {\n this.propagateOnChange(this.value);\n\n this.onChange.emit({\n component: this,\n value: this.value,\n });\n }\n\n _emitSearch() {\n if (!this.canSearch) {\n return;\n }\n\n this.onSearch.emit({\n component: this,\n text: this._searchText,\n });\n }\n\n _emitOnSelect(item: any, isSelected: boolean) {\n this.onSelect.emit({\n component: this,\n item: item,\n isSelected: isSelected,\n });\n }\n\n _emitOnClear(items: any[]) {\n this.onClear.emit({\n component: this,\n items: items,\n });\n }\n\n _emitOnSearchSuccessOrFail(isSuccess: boolean) {\n const eventData = {\n component: this,\n text: this._searchText,\n };\n\n if (isSuccess) {\n this.onSearchSuccess.emit(eventData);\n } else {\n this.onSearchFail.emit(eventData);\n }\n }\n\n _formatItem(item: any): string {\n if (this._isNullOrWhiteSpace(item)) {\n return '';\n }\n\n return this.itemTextField ? item[this.itemTextField] : item.toString();\n }\n\n _formatValueItem(item: any): string {\n if (this._shouldStoreItemValue) {\n // Get item text from the list as we store it's value only.\n const selectedItem = this.items.find((_item) => {\n return _item[this.itemValueField] === item;\n });\n\n return this._formatItem(selectedItem);\n } else {\n return this._formatItem(item);\n }\n }\n\n _getItemValue(item: any): any {\n if (!this._hasObjects) {\n return item;\n }\n\n return item[this.itemValueField];\n }\n\n _getStoredItemValue(item: any): any {\n if (!this._hasObjects) {\n return item;\n }\n\n return this._shouldStoreItemValue ? item : item[this.itemValueField];\n }\n\n _onSearchbarClear() {\n // Ionic Searchbar doesn't clear bind with ngModel value.\n // Do it ourselves.\n this._searchText = '';\n }\n\n _filterItems() {\n this._setHasSearchText();\n\n if (this._hasOnSearch()) {\n // Delegate filtering to the event.\n this._emitSearch();\n } else {\n // Default filtering.\n let groups = [];\n\n if (!this._searchText?.trim()) {\n groups = this._groups;\n } else {\n const filterText = this._searchText.trim().toLowerCase();\n\n this._groups.forEach((group) => {\n const items = group.items.filter((item: any) => {\n const itemText = (\n this.itemTextField ? item[this.itemTextField] : item\n )\n .toString()\n .toLowerCase();\n return itemText.indexOf(filterText) !== -1;\n });\n\n if (items.length) {\n groups.push({\n value: group.value,\n text: group.text,\n items: items,\n });\n }\n });\n\n // No items found.\n if (!groups.length) {\n groups.push({\n items: [],\n });\n }\n }\n\n this._filteredGroups = groups;\n this._hasFilteredItems = !this._areGroupsEmpty(groups);\n this._emitOnSearchSuccessOrFail(this._hasFilteredItems);\n }\n }\n\n _isItemDisabled(item: any): boolean {\n if (!this.disabledItems) {\n return false;\n }\n\n return this.disabledItems.some((_item) => {\n return this._getItemValue(_item) === this._getItemValue(item);\n });\n }\n\n _isItemSelected(item: any) {\n return (\n this._selectedItems.find((selectedItem) => {\n return (\n this._getItemValue(item) === this._getStoredItemValue(selectedItem)\n );\n }) !== undefined\n );\n }\n\n _addSelectedItem(item: any) {\n if (this._shouldStoreItemValue) {\n this._selectedItems.push(this._getItemValue(item));\n } else {\n this._selectedItems.push(item);\n }\n }\n\n _deleteSelectedItem(item: any) {\n let itemToDeleteIndex = -1;\n\n this._selectedItems.forEach((selectedItem, itemIndex) => {\n if (this._getItemValue(item) === this._getStoredItemValue(selectedItem)) {\n itemToDeleteIndex = itemIndex;\n }\n });\n\n this._selectedItems.splice(itemToDeleteIndex, 1);\n }\n\n _click() {\n if (!this.isEnabled) {\n return;\n }\n\n this._label = this._getLabelText();\n this.open().then(() => {\n this.onOpen.emit({\n component: this,\n });\n });\n }\n\n _saveItem(event: Event, item: any) {\n event.stopPropagation();\n this._itemToAdd = item;\n\n if (this._hasOnSaveItem()) {\n this.onSaveItem.emit({\n component: this,\n item: this._itemToAdd,\n });\n } else {\n this.showAddItemTemplate();\n }\n }\n\n _deleteItemClick(event: Event, item: any) {\n event.stopPropagation();\n this._itemToAdd = item;\n\n if (this._hasOnDeleteItem()) {\n // Delegate logic to event.\n this.onDeleteItem.emit({\n component: this,\n item: this._itemToAdd,\n });\n } else {\n this.deleteItem(this._itemToAdd);\n }\n }\n\n _addItemClick() {\n if (this._hasOnAddItem()) {\n