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

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

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

0.41
/projects/igniteui-angular/simple-combo/src/simple-combo/simple-combo.component.ts
1
import { NgTemplateOutlet } from '@angular/common';
2
import { AfterViewInit, Component, DoCheck, EventEmitter, HostListener, Output, ViewChild, inject } from '@angular/core';
3
import { ControlValueAccessor, FormGroupDirective, NG_VALUE_ACCESSOR } from '@angular/forms';
4
import { takeUntil } from 'rxjs/operators';
5

6
import { CancelableEventArgs, IBaseCancelableBrowserEventArgs, IBaseEventArgs, PlatformUtil } from 'igniteui-angular/core';
7
import { IgxButtonDirective } from 'igniteui-angular/directives';
8
import { IgxForOfDirective } from 'igniteui-angular/directives';
9
import { IgxRippleDirective } from 'igniteui-angular/directives';
10
import { IgxTextSelectionDirective } from 'igniteui-angular/directives';
11
import { IgxInputGroupComponent, IgxInputDirective, IgxSuffixDirective } from 'igniteui-angular/input-group';
12
import { IgxIconComponent } from 'igniteui-angular/icon';
13
import { IGX_COMBO_COMPONENT, IgxComboAddItemComponent, IgxComboAPIService, IgxComboBaseDirective, IgxComboDropDownComponent, IgxComboFilteringPipe, IgxComboGroupingPipe, IgxComboItemComponent } from 'igniteui-angular/combo';
14
import { IgxDropDownItemNavigationDirective } from 'igniteui-angular/drop-down';
15

16
/** Emitted when an igx-simple-combo's selection is changing.  */
17
export interface ISimpleComboSelectionChangingEventArgs extends CancelableEventArgs, IBaseEventArgs {
18
    /** An object which represents the value that is currently selected */
19
    oldValue: any;
20
    /** An object which represents the value that will be selected after this event */
21
    newValue: any;
22
    /** An object which represents the item that is currently selected */
23
    oldSelection: any;
24
    /** An object which represents the item that will be selected after this event */
25
    newSelection: any;
26
    /** The text that will be displayed in the combo text box */
27
    displayText: string;
28
}
29

30
/**
31
 * Represents a drop-down list that provides filtering functionality, allowing users to choose a single option from a predefined list.
32
 *
33
 * @igxModule IgxSimpleComboModule
34
 * @igxTheme igx-combo-theme
35
 * @igxKeywords combobox, single combo selection
36
 * @igxGroup Grids & Lists
37
 *
38
 * @remarks
39
 * It provides the ability to filter items as well as perform single selection on the provided data.
40
 * Additionally, it exposes keyboard navigation and custom styling capabilities.
41
 * @example
42
 * ```html
43
 * <igx-simple-combo [itemsMaxHeight]="250" [data]="locationData"
44
 *  [displayKey]="'field'" [valueKey]="'field'"
45
 *  placeholder="Location" searchPlaceholder="Search...">
46
 * </igx-simple-combo>
47
 * ```
48
 */
49
@Component({
50
    selector: 'igx-simple-combo',
51
    templateUrl: 'simple-combo.component.html',
52
    providers: [
53
        IgxComboAPIService,
54
        { provide: IGX_COMBO_COMPONENT, useExisting: IgxSimpleComboComponent },
55
        { provide: NG_VALUE_ACCESSOR, useExisting: IgxSimpleComboComponent, multi: true }
56
    ],
57
    imports: [IgxInputGroupComponent, IgxInputDirective, IgxTextSelectionDirective, IgxSuffixDirective, NgTemplateOutlet, IgxIconComponent, IgxComboDropDownComponent, IgxDropDownItemNavigationDirective, IgxForOfDirective, IgxComboItemComponent, IgxComboAddItemComponent, IgxButtonDirective, IgxRippleDirective, IgxComboFilteringPipe, IgxComboGroupingPipe]
58
})
59
export class IgxSimpleComboComponent extends IgxComboBaseDirective implements ControlValueAccessor, AfterViewInit, DoCheck {
3✔
UNCOV
60
    private platformUtil = inject(PlatformUtil);
×
UNCOV
61
    private formGroupDirective = inject(FormGroupDirective, { optional: true });
×
62

63
    /** @hidden @internal */
64
    @ViewChild(IgxComboDropDownComponent, { static: true })
65
    public dropdown: IgxComboDropDownComponent;
66

67
    /** @hidden @internal */
68
    @ViewChild(IgxComboAddItemComponent)
69
    public addItem: IgxComboAddItemComponent;
70

71
    /**
72
     * Emitted when item selection is changing, before the selection completes
73
     *
74
     * ```html
75
     * <igx-simple-combo (selectionChanging)='handleSelection()'></igx-simple-combo>
76
     * ```
77
     */
78
    @Output()
UNCOV
79
    public selectionChanging = new EventEmitter<ISimpleComboSelectionChangingEventArgs>();
×
80

81
    @ViewChild(IgxTextSelectionDirective, { static: true })
82
    private textSelection: IgxTextSelectionDirective;
83

84
    public override get value(): any {
UNCOV
85
        return this._value[0];
×
86
    }
87

88
    /**
89
     * Get current selection state
90
     *
91
     * @returns The selected item, if any
92
     * ```typescript
93
     * let mySelection = this.combo.selection;
94
     * ```
95
     */
96
    public override get selection(): any {
UNCOV
97
        return super.selection[0];
×
98
    }
99

100
    /** @hidden @internal */
UNCOV
101
    public composing = false;
×
102

UNCOV
103
    private _updateInput = true;
×
104

UNCOV
105
    private _collapsing = false;
×
106

107
    /** @hidden @internal */
108
    public get filteredData(): any[] | null {
UNCOV
109
        return this._filteredData;
×
110
    }
111
    /** @hidden @internal */
112
    public set filteredData(val: any[] | null) {
UNCOV
113
        this._filteredData = this.groupKey ? (val || []).filter((e) => e.isHeader !== true) : val;
×
UNCOV
114
        this.checkMatch();
×
115
    }
116

117
    /** @hidden @internal */
118
    public override get searchValue(): string {
UNCOV
119
        return this._searchValue;
×
120
    }
121
    public override set searchValue(val: string) {
UNCOV
122
        this._searchValue = val;
×
123
    }
124

125
    private get selectedItem(): any {
UNCOV
126
        return this.selectionService.get(this.id).values().next().value;
×
127
    }
128

129
    protected get hasSelectedItem(): boolean {
UNCOV
130
        return !!this.selectionService.get(this.id).size;
×
131
    }
132

133
    constructor() {
UNCOV
134
        super();
×
UNCOV
135
        this.comboAPI.register(this);
×
136
    }
137

138
    /** @hidden @internal */
139
    @HostListener('keydown.ArrowDown', ['$event'])
140
    @HostListener('keydown.Alt.ArrowDown', ['$event'])
141
    public onArrowDown(event: Event): void {
UNCOV
142
        if (this.collapsed) {
×
UNCOV
143
            event.preventDefault();
×
UNCOV
144
            event.stopPropagation();
×
UNCOV
145
            this.open();
×
146
        } else {
UNCOV
147
            if (this.virtDir.igxForOf.length > 0 && !this.hasSelectedItem) {
×
UNCOV
148
                this.dropdown.navigateNext();
×
UNCOV
149
                this.dropdownContainer.nativeElement.focus();
×
UNCOV
150
            } else if (this.allowCustomValues) {
×
UNCOV
151
                this.addItem?.element.nativeElement.focus();
×
152
            }
153
        }
154
    }
155

156
    /**
157
     * Select a defined item
158
     *
159
     * @param item the item to be selected
160
     * ```typescript
161
     * this.combo.select("New York");
162
     * ```
163
     */
164
    public select(item: any): void {
UNCOV
165
        if (item !== undefined) {
×
UNCOV
166
            const newSelection = this.selectionService.add_items(this.id, item instanceof Array ? item : [item], true);
×
UNCOV
167
            this.setSelection(newSelection);
×
168
        }
169
    }
170

171
    /**
172
     * Deselect the currently selected item
173
     *
174
     * @param item the items to be deselected
175
     * ```typescript
176
     * this.combo.deselect("New York");
177
     * ```
178
     */
179
    public deselect(): void {
UNCOV
180
        this.clearSelection();
×
181
    }
182

183
    /** @hidden @internal */
184
    public writeValue(value: any): void {
UNCOV
185
        const oldSelection = super.selection;
×
UNCOV
186
        this.selectionService.select_items(this.id, this.isValid(value) ? [value] : [], true);
×
UNCOV
187
        this.cdr.markForCheck();
×
UNCOV
188
        this._displayValue = this.createDisplayText(super.selection, oldSelection);
×
UNCOV
189
        this._value = this.valueKey ? super.selection.map(item => item[this.valueKey]) : super.selection;
×
UNCOV
190
        this.filterValue = this._displayValue?.toString() || '';
×
191
    }
192

193
    /** @hidden @internal */
194
    public override ngAfterViewInit(): void {
UNCOV
195
        this.virtDir.contentSizeChange.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
196
            if (super.selection.length > 0) {
×
UNCOV
197
                const index = this.virtDir.igxForOf.findIndex(e => {
×
UNCOV
198
                    let current = e ? e[this.valueKey] : undefined;
×
UNCOV
199
                    if (this.valueKey === null || this.valueKey === undefined) {
×
200
                        current = e;
×
201
                    }
UNCOV
202
                    return current === super.selection[0];
×
203
                });
UNCOV
204
                if (!this.isRemote) {
×
205
                    // navigate to item only if we have local data
206
                    // as with remote data this will fiddle with igxFor's scroll handler
207
                    // and will trigger another chunk load which will break the visualization
208
                    this.dropdown.navigateItem(index);
×
209
                }
210
            }
211
        });
UNCOV
212
        this.dropdown.opening.pipe(takeUntil(this.destroy$)).subscribe((args) => {
×
UNCOV
213
            if (args.cancel) {
×
214
                return;
×
215
            }
UNCOV
216
            this._collapsing = false;
×
UNCOV
217
            const filtered = this.filteredData.find(this.findAllMatches);
×
UNCOV
218
            if (filtered === undefined || filtered === null) {
×
UNCOV
219
                this.filterValue = this.searchValue = this.comboInput.value;
×
UNCOV
220
                return;
×
221
            }
222
        });
UNCOV
223
        this.dropdown.opened.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
224
            if (this.composing) {
×
UNCOV
225
                this.comboInput.focus();
×
226
            }
227
        });
UNCOV
228
        this.dropdown.closing.pipe(takeUntil(this.destroy$)).subscribe((args) => {
×
UNCOV
229
            if (args.cancel) {
×
230
                return;
×
231
            }
UNCOV
232
            if (this.getEditElement() && !args.event) {
×
UNCOV
233
                this._collapsing = true;
×
234
            } else {
235
                this.clearOnBlur();
×
236
                this._onTouchedCallback();
×
237
            }
UNCOV
238
            this.comboInput.focus();
×
239
        });
240

241
        // in reactive form the control is not present initially
242
        // and sets the selection to an invalid value in writeValue method
UNCOV
243
        if (!this.isValid(this.selectedItem)) {
×
UNCOV
244
            this.selectionService.clear(this.id);
×
UNCOV
245
            this._displayValue = '';
×
246
        }
247

UNCOV
248
        super.ngAfterViewInit();
×
249
    }
250

251
    /** @hidden @internal */
252
    public ngDoCheck(): void {
UNCOV
253
        if (this.data?.length && super.selection.length && !this._displayValue) {
×
UNCOV
254
            this._displayValue = this.createDisplayText(super.selection, []);
×
UNCOV
255
            this._value = this.valueKey ? super.selection.map(item => item[this.valueKey]) : super.selection;
×
256
        }
257
    }
258

259
    /** @hidden @internal */
260
    public override handleInputChange(event?: any): void {
UNCOV
261
        if (this.collapsed && this.comboInput.focused) {
×
UNCOV
262
            this.open();
×
263
        }
UNCOV
264
        if (event !== undefined) {
×
UNCOV
265
            this.filterValue = this.searchValue = typeof event === 'string' ? event : event.target.value;
×
266
        }
UNCOV
267
        if (!this.comboInput.value.trim() && super.selection.length) {
×
268
            // handle clearing of input by space
UNCOV
269
            this.clearSelection();
×
UNCOV
270
            this._onChangeCallback(null);
×
UNCOV
271
            this.filterValue = '';
×
272
        }
UNCOV
273
        if (super.selection.length) {
×
UNCOV
274
            const args: ISimpleComboSelectionChangingEventArgs = {
×
275
                newValue: undefined,
276
                oldValue: this.selectedItem,
277
                newSelection: undefined,
278
                oldSelection: this.selection,
279
                displayText: typeof event === 'string' ? event : event?.target?.value,
×
280
                owner: this,
281
                cancel: false
282
            };
UNCOV
283
            this.selectionChanging.emit(args);
×
UNCOV
284
            if (!args.cancel) {
×
UNCOV
285
                this.selectionService.select_items(this.id, [], true);
×
286
            }
287
        }
288
        // when filtering the focused item should be the first item or the currently selected item
UNCOV
289
        if (!this.dropdown.focusedItem || this.dropdown.focusedItem.id !== this.dropdown.items[0].id) {
×
UNCOV
290
            this.dropdown.navigateFirst();
×
291
        }
UNCOV
292
        super.handleInputChange(event);
×
UNCOV
293
        this.composing = true;
×
294
    }
295

296
    /** @hidden @internal */
297
    public handleInputClick(): void {
UNCOV
298
        if (this.collapsed) {
×
UNCOV
299
            this.open();
×
UNCOV
300
            this.comboInput.focus();
×
301
        }
302
    }
303

304
    /** @hidden @internal */
305
    public override handleKeyDown(event: KeyboardEvent): void {
UNCOV
306
        if (event.key === this.platformUtil.KEYMAP.ENTER) {
×
UNCOV
307
            const filtered = this.filteredData.find(this.findAllMatches);
×
UNCOV
308
            if (filtered === null || filtered === undefined) {
×
309
                return;
×
310
            }
UNCOV
311
            if (!this.dropdown.collapsed) {
×
UNCOV
312
                const focusedItem = this.dropdown.focusedItem;
×
UNCOV
313
                if (focusedItem && !focusedItem.isHeader) {
×
UNCOV
314
                    this.select(focusedItem.itemID);
×
UNCOV
315
                    event.preventDefault();
×
UNCOV
316
                    event.stopPropagation();
×
UNCOV
317
                    this.close();
×
318
                } else {
UNCOV
319
                    event.preventDefault();
×
UNCOV
320
                    event.stopPropagation();
×
UNCOV
321
                    this.comboInput.focus();
×
322
                }
323
            }
324
            // manually trigger text selection as it will not be triggered during editing
UNCOV
325
            this.textSelection.trigger();
×
UNCOV
326
            return;
×
327
        }
UNCOV
328
        if (event.key === this.platformUtil.KEYMAP.BACKSPACE
×
329
            || event.key === this.platformUtil.KEYMAP.DELETE) {
UNCOV
330
            this._updateInput = false;
×
UNCOV
331
            this.clearSelection(true);
×
332
        }
UNCOV
333
        if (!this.collapsed && event.key === this.platformUtil.KEYMAP.TAB) {
×
UNCOV
334
            const filtered = this.filteredData.find(this.findAllMatches);
×
UNCOV
335
            if (filtered === null || filtered === undefined) {
×
UNCOV
336
                this.clearOnBlur();
×
UNCOV
337
                this.close();
×
UNCOV
338
                return;
×
339
            }
UNCOV
340
            const focusedItem = this.dropdown.focusedItem;
×
UNCOV
341
            if (focusedItem && !focusedItem.isHeader) {
×
UNCOV
342
                this.select(focusedItem.itemID);
×
UNCOV
343
                this.close();
×
UNCOV
344
                this.textSelection.trigger();
×
345
            } else {
UNCOV
346
                this.clearOnBlur();
×
UNCOV
347
                this.close();
×
348
            }
349
        }
UNCOV
350
        this.composing = false;
×
UNCOV
351
        super.handleKeyDown(event);
×
352
    }
353

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

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

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

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

392
    /** @hidden @internal */
393
    public getEditElement(): HTMLElement {
UNCOV
394
        return this.comboInput.nativeElement;
×
395
    }
396

397
    /** @hidden @internal */
398
    public clearInput(event: Event): void {
UNCOV
399
        const oldSelection = this.selection;
×
UNCOV
400
        this.clearSelection(true);
×
401

UNCOV
402
        if (!this.collapsed) {
×
UNCOV
403
            this.focusSearchInput(true);
×
404
        }
UNCOV
405
        event.stopPropagation();
×
406

UNCOV
407
        if (this.selection !== oldSelection) {
×
UNCOV
408
            this.comboInput.value = this.filterValue = this.searchValue = '';
×
409
        }
410

UNCOV
411
        this.dropdown.focusedItem = null;
×
UNCOV
412
        this.composing = false;
×
UNCOV
413
        this.comboInput.focus();
×
414
    }
415

416
    /** @hidden @internal */
417
    public handleClear(event: Event): void {
UNCOV
418
        if (this.disabled) {
×
UNCOV
419
            return;
×
420
        }
421

UNCOV
422
        this.clearInput(event);
×
423
    }
424

425
    /** @hidden @internal */
426
    public handleClearKeyDown(event: KeyboardEvent): void {
UNCOV
427
        if (event.key === 'Enter' || event.key === ' ') {
×
UNCOV
428
            event.preventDefault();
×
UNCOV
429
            this.clearInput(event);
×
430
        }
431
    }
432

433
    /** @hidden @internal */
434
    public handleOpened(): void {
UNCOV
435
        this.triggerCheck();
×
UNCOV
436
        if (!this.comboInput.focused) {
×
UNCOV
437
            this.dropdownContainer.nativeElement.focus();
×
438
        }
UNCOV
439
        this.opened.emit({ owner: this });
×
440
    }
441

442
    /** @hidden @internal */
443
    public override handleClosing(e: IBaseCancelableBrowserEventArgs): void {
UNCOV
444
        const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
×
UNCOV
445
        this.closing.emit(args);
×
UNCOV
446
        e.cancel = args.cancel;
×
UNCOV
447
        if (e.cancel) {
×
UNCOV
448
            return;
×
449
        }
450

UNCOV
451
        this.composing = false;
×
452
        // explicitly update selection so that we don't have to force CD
UNCOV
453
        this.textSelection.selected = true;
×
454
    }
455

456
    /** @hidden @internal */
457
    public focusSearchInput(opening?: boolean): void {
UNCOV
458
        if (opening) {
×
UNCOV
459
            this.dropdownContainer.nativeElement.focus();
×
460
        } else {
UNCOV
461
            this.comboInput.nativeElement.focus();
×
462
        }
463
    }
464

465
    /** @hidden @internal */
466
    public override onClick(event: Event): void {
UNCOV
467
        super.onClick(event);
×
UNCOV
468
        if (this.comboInput.value.length === 0) {
×
UNCOV
469
            this.virtDir.scrollTo(0);
×
470
        }
471
    }
472

UNCOV
473
    protected findAllMatches = (element: any): boolean => {
×
UNCOV
474
        const value = this.displayKey ? element[this.displayKey] : element;
×
UNCOV
475
        if (value === null || value === undefined || value === '') {
×
476
            // we can accept null, undefined and empty strings as empty display values
UNCOV
477
            return true;
×
478
        }
UNCOV
479
        const searchValue = this.searchValue || this.comboInput.value;
×
UNCOV
480
        return !!searchValue && value.toString().toLowerCase().includes(searchValue.toLowerCase());
×
481
    };
482

483
    protected setSelection(newSelection: any): void {
UNCOV
484
        const newValueAsArray = newSelection ? Array.from(newSelection) as IgxComboItemComponent[] : [];
×
UNCOV
485
        const oldValueAsArray = Array.from(this.selectionService.get(this.id) || []);
×
UNCOV
486
        const newItems = this.convertKeysToItems(newValueAsArray);
×
UNCOV
487
        const oldItems = this.convertKeysToItems(oldValueAsArray);
×
UNCOV
488
        const displayText = this.createDisplayText(this.convertKeysToItems(newValueAsArray), oldValueAsArray);
×
UNCOV
489
        const args: ISimpleComboSelectionChangingEventArgs = {
×
490
            newValue: newValueAsArray[0],
491
            oldValue: oldValueAsArray[0],
492
            newSelection: newItems[0],
493
            oldSelection: oldItems[0],
494
            displayText,
495
            owner: this,
496
            cancel: false
497
        };
UNCOV
498
        if (args.newSelection !== args.oldSelection) {
×
UNCOV
499
            this.selectionChanging.emit(args);
×
500
        }
501
        // TODO: refactor below code as it sets the selection and the display text
UNCOV
502
        if (!args.cancel) {
×
UNCOV
503
            let argsSelection = this.isValid(args.newValue)
×
504
                ? args.newValue
505
                : [];
UNCOV
506
            argsSelection = Array.isArray(argsSelection) ? argsSelection : [argsSelection];
×
UNCOV
507
            this.selectionService.select_items(this.id, argsSelection, true);
×
UNCOV
508
            this._value = argsSelection;
×
UNCOV
509
            if (this._updateInput) {
×
UNCOV
510
                this.comboInput.value = this._displayValue = this.searchValue = displayText !== args.displayText
×
511
                    ? args.displayText
512
                    : this.createDisplayText(super.selection, [args.oldValue]);
513
            }
UNCOV
514
            this._onChangeCallback(args.newValue);
×
UNCOV
515
            this._updateInput = true;
×
UNCOV
516
        } else if (this.isRemote) {
×
UNCOV
517
            this.registerRemoteEntries(newValueAsArray, false);
×
518
        } else {
UNCOV
519
            args.displayText = this.createDisplayText(oldItems, []);
×
520

UNCOV
521
            const oldSelectionArray = args.oldSelection ? [args.oldSelection] : [];
×
UNCOV
522
            this.comboInput.value = this._displayValue = this.searchValue = this.createDisplayText(oldSelectionArray, []);
×
523

UNCOV
524
            if (this.isRemote) {
×
525
                this.registerRemoteEntries(newValueAsArray, false);
×
526
            }
527
        }
528
    }
529

530
    protected createDisplayText(newSelection: any[], oldSelection: any[]): string {
UNCOV
531
        if (this.isRemote) {
×
UNCOV
532
            const selection = this.valueKey ? newSelection.map(item => item[this.valueKey]) : newSelection;
×
UNCOV
533
            return this.getRemoteSelection(selection, oldSelection);
×
534
        }
535

UNCOV
536
        if (this.displayKey !== null
×
537
            && this.displayKey !== undefined
538
            && newSelection.length > 0) {
UNCOV
539
            return newSelection.filter(e => e).map(e => e[this.displayKey])[0]?.toString() || '';
×
540
        }
541

UNCOV
542
        return newSelection[0]?.toString() || '';
×
543
    }
544

545
    protected override getRemoteSelection(newSelection: any[], oldSelection: any[]): string {
UNCOV
546
        if (!newSelection.length) {
×
UNCOV
547
            this.registerRemoteEntries(oldSelection, false);
×
UNCOV
548
            return '';
×
549
        }
550

UNCOV
551
        this.registerRemoteEntries(oldSelection, false);
×
UNCOV
552
        this.registerRemoteEntries(newSelection);
×
UNCOV
553
        return Object.keys(this._remoteSelection).map(e => this._remoteSelection[e])[0] || '';
×
554
    }
555

556
    /** Contains key-value pairs of the selected valueKeys and their resp. displayKeys */
557
    protected override registerRemoteEntries(ids: any[], add = true) {
×
UNCOV
558
        const selection = this.getValueDisplayPairs(ids)[0];
×
559

UNCOV
560
        if (add && selection) {
×
UNCOV
561
            this._remoteSelection[selection[this.valueKey]] = selection[this.displayKey].toString();
×
562
        } else {
UNCOV
563
            this._remoteSelection = {};
×
564
        }
565
    }
566

567
    private clearSelection(ignoreFilter?: boolean): void {
UNCOV
568
        let newSelection = this.selectionService.get_empty();
×
UNCOV
569
        if (this.filteredData.length !== this.data.length && !ignoreFilter) {
×
UNCOV
570
            newSelection = this.selectionService.delete_items(this.id, this.selectionService.get_all_ids(this.filteredData, this.valueKey));
×
571
        }
UNCOV
572
        if (this.selectionService.get(this.id).size > 0 || this.comboInput.value.trim()) {
×
UNCOV
573
            this.setSelection(newSelection);
×
574
        }
575
    }
576

577
    private clearOnBlur(): void {
UNCOV
578
        if (this.isRemote) {
×
UNCOV
579
            const searchValue = this.searchValue || this.comboInput.value;
×
UNCOV
580
            const remoteValue = Object.keys(this._remoteSelection).map(e => this._remoteSelection[e])[0] || '';
×
UNCOV
581
            if (searchValue !== remoteValue) {
×
582
                this.clear();
×
583
            }
UNCOV
584
            return;
×
585
        }
586

UNCOV
587
        const filtered = this.filteredData.find(this.findMatch);
×
588
        // selecting null in primitive data returns undefined as the search text is '', but the item is null
UNCOV
589
        if (filtered === undefined && this.selectedItem !== null || !super.selection.length) {
×
UNCOV
590
            this.clear();
×
591
        }
592
    }
593

594
    private getElementVal(element: any): string {
595
        const elementVal = this.displayKey ? element[this.displayKey] : element;
×
596
        return String(elementVal);
×
597
    }
598

599
    private clear(): void {
UNCOV
600
        this.clearSelection(true);
×
UNCOV
601
        const oldSelection = this.selection;
×
UNCOV
602
        if (this.selection !== oldSelection) {
×
603
            this.comboInput.value = this._displayValue = this.searchValue = '';
×
604
        }
605
    }
606

607
    private isValid(value: any): boolean {
UNCOV
608
        if (this.formGroupDirective && value === null) {
×
UNCOV
609
            return false;
×
610
        }
611

UNCOV
612
        if (this.required) {
×
UNCOV
613
            return value !== null && value !== '' && value !== undefined
×
614
        }
615

UNCOV
616
        return value !== undefined;
×
617
    }
618
}
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