• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 9593874219

20 Jun 2024 08:08AM UTC coverage: 91.703% (-0.02%) from 91.721%
9593874219

push

github

web-flow
Merge pull request #14343 from IgniteUI/ganastasov/fix-14200-18.0

fix(simple-combo): trigger selectionChanging on input clear - 18.0

12490 of 14585 branches covered (85.64%)

13 of 15 new or added lines in 1 file covered. (86.67%)

5 existing lines in 2 files now uncovered.

25541 of 27852 relevant lines covered (91.7%)

32411.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.72
/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts
1
import { DOCUMENT, NgIf, NgTemplateOutlet } from '@angular/common';
2
import {
3
    AfterViewInit, ChangeDetectorRef, Component, DoCheck, ElementRef, EventEmitter, HostListener, Inject, Injector,
4
    Optional, Output, ViewChild
5
} from '@angular/core';
6
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
7
import { takeUntil } from 'rxjs/operators';
8

9
import { IgxComboAddItemComponent } from '../combo/combo-add-item.component';
10
import { IgxComboDropDownComponent } from '../combo/combo-dropdown.component';
11
import { IgxComboItemComponent } from '../combo/combo-item.component';
12
import { IgxComboAPIService } from '../combo/combo.api';
13
import { IgxComboBaseDirective, IGX_COMBO_COMPONENT } from '../combo/combo.common';
14
import { IgxSelectionAPIService } from '../core/selection';
15
import { CancelableEventArgs, IBaseCancelableBrowserEventArgs, IBaseEventArgs, PlatformUtil } from '../core/utils';
16
import { IgxButtonDirective } from '../directives/button/button.directive';
17
import { IgxForOfDirective } from '../directives/for-of/for_of.directive';
18
import { IgxRippleDirective } from '../directives/ripple/ripple.directive';
19
import { IgxTextSelectionDirective } from '../directives/text-selection/text-selection.directive';
20
import { IgxIconService } from '../icon/icon.service';
21
import { IgxInputGroupType, IGX_INPUT_GROUP_TYPE } from '../input-group/public_api';
22
import { IgxComboFilteringPipe, IgxComboGroupingPipe } from '../combo/combo.pipes';
23
import { IgxDropDownItemNavigationDirective } from '../drop-down/drop-down-navigation.directive';
24
import { IgxIconComponent } from '../icon/icon.component';
25
import { IgxSuffixDirective } from '../directives/suffix/suffix.directive';
26
import { IgxInputDirective } from '../directives/input/input.directive';
27
import { IgxInputGroupComponent } from '../input-group/input-group.component';
28

29
/** Emitted when an igx-simple-combo's selection is changing.  */
30
export interface ISimpleComboSelectionChangingEventArgs extends CancelableEventArgs, IBaseEventArgs {
31
    /** An object which represents the value that is currently selected */
32
    oldValue: any;
33
    /** An object which represents the value that will be selected after this event */
34
    newValue: any;
35
    /** An object which represents the item that is currently selected */
36
    oldSelection: any;
37
    /** An object which represents the item that will be selected after this event */
38
    newSelection: any;
39
    /** The text that will be displayed in the combo text box */
40
    displayText: string;
41
}
42

43
/**
44
 * Represents a drop-down list that provides filtering functionality, allowing users to choose a single option from a predefined list.
45
 *
46
 * @igxModule IgxSimpleComboModule
47
 * @igxTheme igx-combo-theme
48
 * @igxKeywords combobox, single combo selection
49
 * @igxGroup Grids & Lists
50
 *
51
 * @remarks
52
 * It provides the ability to filter items as well as perform single selection on the provided data.
53
 * Additionally, it exposes keyboard navigation and custom styling capabilities.
54
 * @example
55
 * ```html
56
 * <igx-simple-combo [itemsMaxHeight]="250" [data]="locationData"
57
 *  [displayKey]="'field'" [valueKey]="'field'"
58
 *  placeholder="Location" searchPlaceholder="Search...">
59
 * </igx-simple-combo>
60
 * ```
61
 */
62
@Component({
63
    selector: 'igx-simple-combo',
64
    templateUrl: 'simple-combo.component.html',
65
    providers: [
66
        IgxComboAPIService,
67
        { provide: IGX_COMBO_COMPONENT, useExisting: IgxSimpleComboComponent },
68
        { provide: NG_VALUE_ACCESSOR, useExisting: IgxSimpleComboComponent, multi: true }
69
    ],
70
    standalone: true,
71
    imports: [IgxInputGroupComponent, IgxInputDirective, IgxTextSelectionDirective, NgIf, IgxSuffixDirective, NgTemplateOutlet, IgxIconComponent, IgxComboDropDownComponent, IgxDropDownItemNavigationDirective, IgxForOfDirective, IgxComboItemComponent, IgxComboAddItemComponent, IgxButtonDirective, IgxRippleDirective, IgxComboFilteringPipe, IgxComboGroupingPipe]
72
})
73
export class IgxSimpleComboComponent extends IgxComboBaseDirective implements ControlValueAccessor, AfterViewInit, DoCheck {
2✔
74
    /** @hidden @internal */
75
    @ViewChild(IgxComboDropDownComponent, { static: true })
76
    public dropdown: IgxComboDropDownComponent;
77

78
    /** @hidden @internal */
79
    @ViewChild(IgxComboAddItemComponent)
80
    public addItem: IgxComboAddItemComponent;
81

82
    /**
83
     * Emitted when item selection is changing, before the selection completes
84
     *
85
     * ```html
86
     * <igx-simple-combo (selectionChanging)='handleSelection()'></igx-simple-combo>
87
     * ```
88
     */
89
    @Output()
90
    public selectionChanging = new EventEmitter<ISimpleComboSelectionChangingEventArgs>();
92✔
91

92
    @ViewChild(IgxTextSelectionDirective, { static: true })
93
    private textSelection: IgxTextSelectionDirective;
94

95
    public override get value(): any {
96
        return this._value[0];
58✔
97
    }
98

99
    /**
100
     * Get current selection state
101
     *
102
     * @returns The selected item, if any
103
     * ```typescript
104
     * let mySelection = this.combo.selection;
105
     * ```
106
     */
107
    public override get selection(): any {
108
        return super.selection[0];
136✔
109
    }
110

111
    /** @hidden @internal */
112
    public composing = false;
92✔
113

114
    private _updateInput = true;
92✔
115

116
    private _collapsing = false;
92✔
117

118
    /** @hidden @internal */
119
    public get filteredData(): any[] | null {
120
        return this._filteredData;
1,137✔
121
    }
122
    /** @hidden @internal */
123
    public set filteredData(val: any[] | null) {
124
        this._filteredData = this.groupKey ? (val || []).filter((e) => e.isHeader !== true) : val;
5,412!
125
        this.checkMatch();
207✔
126
    }
127

128
    /** @hidden @internal */
129
    public override get searchValue(): string {
130
        return this._searchValue;
10,053✔
131
    }
132
    public override set searchValue(val: string) {
133
        this._searchValue = val;
201✔
134
    }
135

136
    private get selectedItem(): any {
137
        return this.selectionService.get(this.id).values().next().value;
96✔
138
    }
139

140
    protected get hasSelectedItem(): boolean {
141
        return !!this.selectionService.get(this.id).size;
713✔
142
    }
143

144
    constructor(elementRef: ElementRef,
145
        cdr: ChangeDetectorRef,
146
        selectionService: IgxSelectionAPIService,
147
        comboAPI: IgxComboAPIService,
148
        _iconService: IgxIconService,
149
        private platformUtil: PlatformUtil,
92✔
150
        @Inject(DOCUMENT) document: any,
151
        @Optional() @Inject(IGX_INPUT_GROUP_TYPE) _inputGroupType: IgxInputGroupType,
152
        @Optional() _injector: Injector) {
153
        super(elementRef, cdr, selectionService, comboAPI,
92✔
154
            _iconService, document, _inputGroupType, _injector);
155
        this.comboAPI.register(this);
92✔
156
    }
157

158
    /** @hidden @internal */
159
    @HostListener('keydown.ArrowDown', ['$event'])
160
    @HostListener('keydown.Alt.ArrowDown', ['$event'])
161
    public onArrowDown(event: Event): void {
162
        if (this.collapsed) {
5✔
163
            event.preventDefault();
2✔
164
            event.stopPropagation();
2✔
165
            this.open();
2✔
166
        } else {
167
            if (this.virtDir.igxForOf.length > 0 && !this.hasSelectedItem) {
3✔
168
                this.dropdown.navigateNext();
2✔
169
                this.dropdownContainer.nativeElement.focus();
2✔
170
            } else if (this.allowCustomValues) {
1✔
171
                this.addItem?.element.nativeElement.focus();
1✔
172
            }
173
        }
174
    }
175

176
    /**
177
     * Select a defined item
178
     *
179
     * @param item the item to be selected
180
     * ```typescript
181
     * this.combo.select("New York");
182
     * ```
183
     */
184
    public select(item: any): void {
185
        if (item !== undefined) {
63✔
186
            const newSelection = this.selectionService.add_items(this.id, item instanceof Array ? item : [item], true);
63✔
187
            this.setSelection(newSelection);
63✔
188
        }
189
    }
190

191
    /**
192
     * Deselect the currently selected item
193
     *
194
     * @param item the items to be deselected
195
     * ```typescript
196
     * this.combo.deselect("New York");
197
     * ```
198
     */
199
    public deselect(): void {
200
        this.clearSelection();
4✔
201
    }
202

203
    /** @hidden @internal */
204
    public writeValue(value: any): void {
205
        const oldSelection = super.selection;
58✔
206
        this.selectionService.select_items(this.id, this.isValid(value) ? [value] : [], true);
57✔
207
        this.cdr.markForCheck();
57✔
208
        this._displayValue = this.createDisplayText(super.selection, oldSelection);
57✔
209
        this._value = this.valueKey ? super.selection.map(item => item[this.valueKey]) : super.selection;
57✔
210
        this.filterValue = this._displayValue?.toString() || '';
57✔
211
    }
212

213
    /** @hidden @internal */
214
    public override ngAfterViewInit(): void {
215
        this.virtDir.contentSizeChange.pipe(takeUntil(this.destroy$)).subscribe(() => {
77✔
216
            if (super.selection.length > 0) {
1✔
217
                const index = this.virtDir.igxForOf.findIndex(e => {
1✔
218
                    let current = e? e[this.valueKey] : undefined;
10!
219
                    if (this.valueKey === null || this.valueKey === undefined) {
10!
220
                        current = e;
×
221
                    }
222
                    return current === super.selection[0];
10✔
223
                });
224
                if (!this.isRemote) {
1!
225
                    // navigate to item only if we have local data
226
                    // as with remote data this will fiddle with igxFor's scroll handler
227
                    // and will trigger another chunk load which will break the visualization
228
                    this.dropdown.navigateItem(index);
×
229
                }
230
            }
231
        });
232
        this.dropdown.opening.pipe(takeUntil(this.destroy$)).subscribe((args) => {
77✔
233
            if (args.cancel) {
64!
234
                return;
×
235
            }
236
            this._collapsing = false;
64✔
237
            const filtered = this.filteredData.find(this.findAllMatches);
64✔
238
            if (filtered === undefined || filtered === null) {
64✔
239
                this.filterValue = this.searchValue = this.comboInput.value;
37✔
240
                return;
37✔
241
            }
242
            this.filterValue = this.searchValue = '';
27✔
243
        });
244
        this.dropdown.opened.pipe(takeUntil(this.destroy$)).subscribe(() => {
77✔
245
            if (this.composing) {
20✔
246
                this.comboInput.focus();
4✔
247
            }
248
        });
249
        this.dropdown.closing.pipe(takeUntil(this.destroy$)).subscribe((args) => {
77✔
250
            if (args.cancel) {
29!
251
                return;
×
252
            }
253
            if (this.getEditElement() && !args.event) {
29✔
254
                this._collapsing = true;
28✔
255
            } else {
256
                this.clearOnBlur();
1✔
257
                this._onTouchedCallback();
1✔
258
            }
259
            this.comboInput.focus();
29✔
260
        });
261

262
        // in reactive form the control is not present initially
263
        // and sets the selection to an invalid value in writeValue method
264
        if (!this.isValid(this.selectedItem)) {
77✔
265
            this.selectionService.clear(this.id);
70✔
266
            this._displayValue = '';
70✔
267
        }
268

269
        super.ngAfterViewInit();
77✔
270
    }
271

272
    /** @hidden @internal */
273
    public ngDoCheck(): void {
274
        if (this.data?.length && super.selection.length && !this._displayValue) {
301✔
275
            this._displayValue = this.createDisplayText(super.selection, []);
10✔
276
            this._value = this.valueKey ? super.selection.map(item => item[this.valueKey]) : super.selection;
10✔
277
        }
278
    }
279

280
    /** @hidden @internal */
281
    public override handleInputChange(event?: any): void {
282
        if (event !== undefined) {
45✔
283
            this.filterValue = this.searchValue = typeof event === 'string' ? event : event.target.value;
43✔
284
        }
285
        if (this.collapsed && this.comboInput.focused) {
45✔
286
            this.open();
10✔
287
        }
288
        if (!this.comboInput.value.trim() && super.selection.length) {
45✔
289
            // handle clearing of input by space
290
            this.clearSelection();
1✔
291
            this._onChangeCallback(null);
1✔
292
            this.filterValue = '';
1✔
293
        }
294
        if (super.selection.length) {
45✔
295
            const args: ISimpleComboSelectionChangingEventArgs = {
7✔
296
                newValue: undefined,
297
                oldValue: this.selectedItem,
298
                newSelection: undefined,
299
                oldSelection: this.selection,
300
                displayText: typeof event === 'string' ? event : event?.target?.value,
7✔
301
                owner: this,
302
                cancel: false
303
            };
304
            this.selectionChanging.emit(args);
7✔
305
            if (!args.cancel) {
7✔
306
                this.selectionService.select_items(this.id, [], true);
3✔
307
            }
308
        }
309
        // when filtering the focused item should be the first item or the currently selected item
310
        if (!this.dropdown.focusedItem || this.dropdown.focusedItem.id !== this.dropdown.items[0].id) {
45✔
311
            this.dropdown.navigateFirst();
24✔
312
        }
313
        super.handleInputChange(event);
45✔
314
        this.composing = true;
45✔
315
    }
316

317
    /** @hidden @internal */
318
    public handleInputClick(): void {
319
        if (this.collapsed) {
3✔
320
            this.open();
1✔
321
            this.comboInput.focus();
1✔
322
        }
323
    }
324

325
    /** @hidden @internal */
326
    public override handleKeyDown(event: KeyboardEvent): void {
327
        if (event.key === this.platformUtil.KEYMAP.ENTER) {
57✔
328
            const filtered = this.filteredData.find(this.findAllMatches);
8✔
329
            if (filtered === null || filtered === undefined) {
8!
330
                return;
×
331
            }
332
            if (!this.dropdown.collapsed) {
8✔
333
                this.select(this.dropdown.focusedItem.itemID);
7✔
334
                event.preventDefault();
7✔
335
                event.stopPropagation();
7✔
336
                this.close();
7✔
337
            }
338
            // manually trigger text selection as it will not be triggered during editing
339
            this.textSelection.trigger();
8✔
340
            return;
8✔
341
        }
342
        if (event.key === this.platformUtil.KEYMAP.BACKSPACE
49✔
343
            || event.key === this.platformUtil.KEYMAP.DELETE) {
344
            this._updateInput = false;
2✔
345
            this.clearSelection(true);
2✔
346
        }
347
        if (!this.collapsed && event.key === this.platformUtil.KEYMAP.TAB) {
49✔
348
            this.clearOnBlur();
10✔
349
            this.close();
10✔
350
        }
351
        this.composing = false;
49✔
352
        super.handleKeyDown(event);
49✔
353
    }
354

355
    /** @hidden @internal */
356
    public handleKeyUp(event: KeyboardEvent): void {
357
        if (event.key === this.platformUtil.KEYMAP.ARROW_DOWN) {
33✔
358
            this.dropdown.focusedItem = this.hasSelectedItem && this.filteredData.length > 0
1!
359
                ? this.dropdown.items.find(i => i.itemID === this.selectedItem)
×
360
                : this.dropdown.items[0];
361
            this.dropdownContainer.nativeElement.focus();
1✔
362
        }
363
    }
364

365
    /** @hidden @internal */
366
    public handleItemKeyDown(event: KeyboardEvent): void {
367
        if (event.key === this.platformUtil.KEYMAP.ARROW_UP && event.altKey) {
4!
368
            this.close();
×
369
            this.comboInput.focus();
×
370
            return;
×
371
        }
372
        if (event.key === this.platformUtil.KEYMAP.ENTER) {
4!
373
            this.comboInput.focus();
×
374
        }
375
    }
376

377
    /** @hidden @internal */
378
    public handleItemClick(): void {
379
        this.close();
26✔
380
        this.comboInput.focus();
26✔
381
    }
382

383
    /** @hidden @internal */
384
    public override onBlur(): void {
385
        // when clicking the toggle button to close the combo and immediately clicking outside of it
386
        // the collapsed state is not modified as the dropdown is still not closed
387
        if (this.collapsed || this._collapsing) {
17✔
388
            this.clearOnBlur();
13✔
389
        }
390
        super.onBlur();
17✔
391
    }
392

393
    /** @hidden @internal */
394
    public getEditElement(): HTMLElement {
395
        return this.comboInput.nativeElement;
29✔
396
    }
397

398
    /** @hidden @internal */
399
    public handleClear(event: Event): void {
400
        if (this.disabled) {
7✔
401
            return;
1✔
402
        }
403

404
        const oldSelection = this.selection;
6✔
405
        this.clearSelection(true);
6✔
406

407
        if(!this.collapsed){
6✔
408
            this.focusSearchInput(true);
2✔
409
        }
410
        event.stopPropagation();
6✔
411

412
        if (this.selection !== oldSelection) {
6✔
413
            this.comboInput.value = this.filterValue = this.searchValue = '';
5✔
414
        }
415

416
        this.dropdown.focusedItem = null;
6✔
417
        this.composing = false;
6✔
418
        this.comboInput.focus();
6✔
419
    }
420

421
    /** @hidden @internal */
422
    public handleOpened(): void {
423
        this.triggerCheck();
20✔
424
        if (!this.comboInput.focused) {
20✔
425
            this.dropdownContainer.nativeElement.focus();
17✔
426
        }
427
        this.opened.emit({ owner: this });
20✔
428
    }
429

430
    /** @hidden @internal */
431
    public override handleClosing(e: IBaseCancelableBrowserEventArgs): void {
432
        const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
31✔
433
        this.closing.emit(args);
31✔
434
        e.cancel = args.cancel;
31✔
435
        if (e.cancel) {
31✔
436
            return;
1✔
437
        }
438

439
        this.composing = false;
30✔
440
        // explicitly update selection and trigger text selection so that we don't have to force CD
441
        this.textSelection.selected = true;
30✔
442
        this.textSelection.trigger();
30✔
443
    }
444

445
    /** @hidden @internal */
446
    public focusSearchInput(opening?: boolean): void {
447
        if (opening) {
6✔
448
            this.dropdownContainer.nativeElement.focus();
2✔
449
        } else {
450
            this.comboInput.nativeElement.focus();
4✔
451
        }
452
    }
453

454
    /** @hidden @internal */
455
    public override onClick(event: Event): void {
456
        super.onClick(event);
5✔
457
        if (this.comboInput.value.length === 0) {
5✔
458
            this.virtDir.scrollTo(0);
3✔
459
        }
460
    }
461

462
    protected findAllMatches = (element: any): boolean => {
92✔
463
        const value = this.displayKey ? element[this.displayKey] : element;
1,442✔
464
        if (value === null || value === undefined || value === '') {
1,442✔
465
            // we can accept null, undefined and empty strings as empty display values
466
            return true;
13✔
467
        }
468
        const searchValue = this.searchValue || this.comboInput.value;
1,429✔
469
        return !!searchValue && value.toString().toLowerCase().includes(searchValue.toLowerCase());
1,429✔
470
    };
471

472
    protected setSelection(newSelection: any): void {
473
        const newValueAsArray = newSelection ? Array.from(newSelection) as IgxComboItemComponent[] : [];
92✔
474
        const oldValueAsArray = Array.from(this.selectionService.get(this.id) || []);
92!
475
        const newItems = this.convertKeysToItems(newValueAsArray);
92✔
476
        const oldItems = this.convertKeysToItems(oldValueAsArray);
92✔
477
        const displayText = this.createDisplayText(this.convertKeysToItems(newValueAsArray), oldValueAsArray);
92✔
478
        const args: ISimpleComboSelectionChangingEventArgs = {
92✔
479
            newValue: newValueAsArray[0],
480
            oldValue: oldValueAsArray[0],
481
            newSelection: newItems[0],
482
            oldSelection: oldItems[0],
483
            displayText,
484
            owner: this,
485
            cancel: false
486
        };
487
        if (args.newSelection !== args.oldSelection) {
92✔
488
            this.selectionChanging.emit(args);
75✔
489
        }
490
        // TODO: refactor below code as it sets the selection and the display text
491
        if (!args.cancel) {
92✔
492
            let argsSelection = this.isValid(args.newValue)
90✔
493
                ? args.newValue
494
                : [];
495
            argsSelection = Array.isArray(argsSelection) ? argsSelection : [argsSelection];
90✔
496
            this.selectionService.select_items(this.id, argsSelection, true);
90✔
497
            this._value = argsSelection;
90✔
498
            if (this._updateInput) {
90✔
499
                this.comboInput.value = this._displayValue = this.searchValue = displayText !== args.displayText
88!
500
                    ? args.displayText
501
                    : this.createDisplayText(super.selection, [args.oldValue]);
502
            }
503
            this._onChangeCallback(args.newValue);
90✔
504
            this._updateInput = true;
90✔
505
        } else if (this.isRemote) {
2✔
506
            this.registerRemoteEntries(newValueAsArray, false);
1✔
507
        } else {
508
            args.displayText = this.createDisplayText(oldItems, []);
1✔
509

510
            const oldSelectionArray = args.oldSelection ? [args.oldSelection] : [];
1!
511
            this.comboInput.value = this._displayValue = this.searchValue = this.createDisplayText(oldSelectionArray, []);
1✔
512

513
            if (this.isRemote) {
1!
NEW
514
                this.registerRemoteEntries(newValueAsArray, false);
×
515
            }
516
        }
517
    }
518

519
    protected createDisplayText(newSelection: any[], oldSelection: any[]): string {
520
        if (this.isRemote) {
249✔
521
            const selection = this.valueKey ? newSelection.map(item => item[this.valueKey]) : newSelection;
31!
522
            return this.getRemoteSelection(selection, oldSelection);
31✔
523
        }
524

525
        if (this.displayKey !== null
218✔
526
            && this.displayKey !== undefined
527
            && newSelection.length > 0) {
528
            return newSelection.filter(e => e).map(e => e[this.displayKey])[0]?.toString() || '';
97✔
529
        }
530

531
        return newSelection[0]?.toString() || '';
121✔
532
    }
533

534
    protected override getRemoteSelection(newSelection: any[], oldSelection: any[]): string {
535
        if (!newSelection.length) {
31✔
536
            this.registerRemoteEntries(oldSelection, false);
8✔
537
            return '';
8✔
538
        }
539

540
        this.registerRemoteEntries(oldSelection, false);
23✔
541
        this.registerRemoteEntries(newSelection);
23✔
542
        return Object.keys(this._remoteSelection).map(e => this._remoteSelection[e])[0] || '';
23✔
543
    }
544

545
    /** Contains key-value pairs of the selected valueKeys and their resp. displayKeys */
546
    protected override registerRemoteEntries(ids: any[], add = true) {
23✔
547
        const selection = this.getValueDisplayPairs(ids)[0];
55✔
548

549
        if (add && selection) {
55✔
550
            this._remoteSelection[selection[this.valueKey]] = selection[this.displayKey].toString();
21✔
551
        } else {
552
            this._remoteSelection = {};
34✔
553
        }
554
    }
555

556
    private clearSelection(ignoreFilter?: boolean): void {
557
        let newSelection = this.selectionService.get_empty();
29✔
558
        if (this.filteredData.length !== this.data.length && !ignoreFilter) {
29✔
559
            newSelection = this.selectionService.delete_items(this.id, this.selectionService.get_all_ids(this.filteredData, this.valueKey));
1✔
560
        }
561
        this.setSelection(newSelection);
29✔
562
    }
563

564
    private clearOnBlur(): void {
565
        if (this.isRemote) {
24✔
566
            const searchValue = this.searchValue || this.comboInput.value;
2!
567
            const remoteValue = Object.keys(this._remoteSelection).map(e => this._remoteSelection[e])[0] || '';
2✔
568
            if (searchValue !== remoteValue) {
2✔
569
                this.clear();
1✔
570
            }
571
            return;
2✔
572
        }
573

574
        const filtered = this.filteredData.find(this.findMatch);
22✔
575
        // selecting null in primitive data returns undefined as the search text is '', but the item is null
576
        if (filtered === undefined && this.selectedItem !== null || !super.selection.length) {
22✔
577
            this.clear();
15✔
578
        }
579
    }
580

581
    private getElementVal(element: any): string {
582
        const elementVal = this.displayKey ? element[this.displayKey] : element;
×
583
        return String(elementVal);
×
584
    }
585

586
    private clear(): void {
587
        this.clearSelection(true);
16✔
588
        const oldSelection = this.selection;
16✔
589
        if (this.selection !== oldSelection) {
16!
NEW
590
            this.comboInput.value = this._displayValue = this.searchValue = '';
×
591
        }
592
    }
593

594
    private isValid(value: any): boolean {
595
        return this.required
224✔
596
        ? value !== null && value !== '' && value !== undefined
124✔
597
        : value !== undefined;
598
    }
599
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc