• 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

4.44
/projects/igniteui-angular/select/src/select/select.component.ts
1
import { AfterContentChecked, AfterContentInit, AfterViewInit, booleanAttribute, Component, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, forwardRef, HostBinding, Injector, Input, OnDestroy, OnInit, Output, QueryList, TemplateRef, ViewChild, ViewChildren, inject } from '@angular/core';
2
import { NgTemplateOutlet } from '@angular/common';
3
import { AbstractControl, ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
4
import { noop } from 'rxjs';
5
import { takeUntil } from 'rxjs/operators';
6

7
import {
8
    EditorProvider,
9
    IBaseCancelableBrowserEventArgs,
10
    IBaseEventArgs,
11
    AbsoluteScrollStrategy,
12
    OverlaySettings
13
} from 'igniteui-angular/core';
14
import { IgxSelectItemComponent } from './select-item.component';
15
import { SelectPositioningStrategy } from './select-positioning-strategy';
16
import { IgxSelectBase } from './select.common';
17
import { IgxHintDirective, IgxInputGroupType, IgxPrefixDirective, IGX_INPUT_GROUP_TYPE, IgxInputGroupComponent, IgxInputDirective, IgxInputState, IgxLabelDirective, IgxReadOnlyInputDirective, IgxSuffixDirective } from 'igniteui-angular/input-group';
18
import { ToggleViewCancelableEventArgs, ToggleViewEventArgs, IgxToggleDirective } from 'igniteui-angular/directives';
19
import { IgxOverlayService } from 'igniteui-angular/core';
20
import { IgxIconComponent } from 'igniteui-angular/icon';
21
import { IgxSelectItemNavigationDirective } from './select-navigation.directive';
22
import { IGX_DROPDOWN_BASE, IgxDropDownComponent, IgxDropDownItemBaseDirective, ISelectionEventArgs, Navigate } from 'igniteui-angular/drop-down';
23

24
/** @hidden @internal */
25
@Directive({
26
    selector: '[igxSelectToggleIcon]',
27
    standalone: true
28
})
29
export class IgxSelectToggleIconDirective {
3✔
30
}
31

32
/** @hidden @internal */
33
@Directive({
34
    selector: '[igxSelectHeader]',
35
    standalone: true
36
})
37
export class IgxSelectHeaderDirective {
3✔
38
}
39

40
/** @hidden @internal */
41
@Directive({
42
    selector: '[igxSelectFooter]',
43
    standalone: true
44
})
45
export class IgxSelectFooterDirective {
3✔
46
}
47

48
/**
49
 * **Ignite UI for Angular Select** -
50
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/select)
51
 *
52
 * The `igxSelect` provides an input with dropdown list allowing selection of a single item.
53
 *
54
 * Example:
55
 * ```html
56
 * <igx-select #select1 [placeholder]="'Pick One'">
57
 *   <label igxLabel>Select Label</label>
58
 *   <igx-select-item *ngFor="let item of items" [value]="item.field">
59
 *     {{ item.field }}
60
 *   </igx-select-item>
61
 * </igx-select>
62
 * ```
63
 */
64
@Component({
65
    selector: 'igx-select',
66
    templateUrl: './select.component.html',
67
    providers: [
68
        { provide: NG_VALUE_ACCESSOR, useExisting: IgxSelectComponent, multi: true },
69
        { provide: IGX_DROPDOWN_BASE, useExisting: IgxSelectComponent }
70
    ],
71
    styles: [`
72
        :host {
73
            display: block;
74
        }
75
    `],
76
    imports: [IgxInputGroupComponent, IgxInputDirective, IgxSelectItemNavigationDirective, IgxSuffixDirective, IgxReadOnlyInputDirective, NgTemplateOutlet, IgxIconComponent, IgxToggleDirective]
77
})
78
export class IgxSelectComponent extends IgxDropDownComponent implements IgxSelectBase, ControlValueAccessor,
3✔
79
    AfterContentInit, OnInit, AfterViewInit, OnDestroy, EditorProvider, AfterContentChecked {
UNCOV
80
    protected overlayService = inject<IgxOverlayService>(IgxOverlayService);
×
UNCOV
81
    private _inputGroupType = inject<IgxInputGroupType>(IGX_INPUT_GROUP_TYPE, { optional: true });
×
UNCOV
82
    private _injector = inject(Injector);
×
83

84

85
    /** @hidden @internal */
86
    @ViewChild('inputGroup', { read: IgxInputGroupComponent, static: true }) public inputGroup: IgxInputGroupComponent;
87

88
    /** @hidden @internal */
89
    @ViewChild('input', { read: IgxInputDirective, static: true }) public input: IgxInputDirective;
90

91
    /** @hidden @internal */
92
    @ContentChildren(forwardRef(() => IgxSelectItemComponent), { descendants: true })
1✔
93
    public override children: QueryList<IgxSelectItemComponent>;
94

95
    @ContentChildren(IgxPrefixDirective, { descendants: true })
96
    protected prefixes: QueryList<IgxPrefixDirective>;
97

98
    @ContentChildren(IgxSuffixDirective, { descendants: true })
99
    protected suffixes: QueryList<IgxSuffixDirective>;
100

101
    @ViewChildren(IgxSuffixDirective)
102
    protected internalSuffixes: QueryList<IgxSuffixDirective>;
103

104
    /** @hidden @internal */
105
    @ContentChild(forwardRef(() => IgxLabelDirective), { static: true }) public label: IgxLabelDirective;
1✔
106

107
    /**
108
     * Sets input placeholder.
109
     *
110
     */
111
    @Input() public placeholder;
112

113

114
    /**
115
     * Disables the component.
116
     * ```html
117
     * <igx-select [disabled]="'true'"></igx-select>
118
     * ```
119
     */
UNCOV
120
    @Input({ transform: booleanAttribute }) public disabled = false;
×
121

122
    /**
123
     * Sets custom OverlaySettings `IgxSelectComponent`.
124
     * ```html
125
     * <igx-select [overlaySettings] = "customOverlaySettings"></igx-select>
126
     * ```
127
     */
128
    @Input()
129
    public overlaySettings: OverlaySettings;
130

131
    /** @hidden @internal */
132
    @HostBinding('style.maxHeight')
UNCOV
133
    public override maxHeight = '256px';
×
134

135
    /**
136
     * Emitted before the dropdown is opened
137
     *
138
     * ```html
139
     * <igx-select opening='handleOpening($event)'></igx-select>
140
     * ```
141
     */
142
    @Output()
UNCOV
143
    public override opening = new EventEmitter<IBaseCancelableBrowserEventArgs>();
×
144

145
    /**
146
     * Emitted after the dropdown is opened
147
     *
148
     * ```html
149
     * <igx-select (opened)='handleOpened($event)'></igx-select>
150
     * ```
151
     */
152
    @Output()
UNCOV
153
    public override opened = new EventEmitter<IBaseEventArgs>();
×
154

155
    /**
156
     * Emitted before the dropdown is closed
157
     *
158
     * ```html
159
     * <igx-select (closing)='handleClosing($event)'></igx-select>
160
     * ```
161
     */
162
    @Output()
UNCOV
163
    public override closing = new EventEmitter<IBaseCancelableBrowserEventArgs>();
×
164

165
    /**
166
     * Emitted after the dropdown is closed
167
     *
168
     * ```html
169
     * <igx-select (closed)='handleClosed($event)'></igx-select>
170
     * ```
171
     */
172
    @Output()
UNCOV
173
    public override closed = new EventEmitter<IBaseEventArgs>();
×
174

175
    /**
176
     * The custom template, if any, that should be used when rendering the select TOGGLE(open/close) button
177
     *
178
     * ```typescript
179
     * // Set in typescript
180
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
181
     * myComponent.select.toggleIconTemplate = myCustomTemplate;
182
     * ```
183
     * ```html
184
     * <!-- Set in markup -->
185
     *  <igx-select #select>
186
     *      ...
187
     *      <ng-template igxSelectToggleIcon let-collapsed>
188
     *          <igx-icon>{{ collapsed ? 'remove_circle' : 'remove_circle_outline'}}</igx-icon>
189
     *      </ng-template>
190
     *  </igx-select>
191
     * ```
192
     */
193
    @ContentChild(IgxSelectToggleIconDirective, { read: TemplateRef })
UNCOV
194
    public toggleIconTemplate: TemplateRef<any> = null;
×
195

196
    /**
197
     * The custom template, if any, that should be used when rendering the HEADER for the select items list
198
     *
199
     * ```typescript
200
     * // Set in typescript
201
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
202
     * myComponent.select.headerTemplate = myCustomTemplate;
203
     * ```
204
     * ```html
205
     * <!-- Set in markup -->
206
     *  <igx-select #select>
207
     *      ...
208
     *      <ng-template igxSelectHeader>
209
     *          <div class="select__header">
210
     *              This is a custom header
211
     *          </div>
212
     *      </ng-template>
213
     *  </igx-select>
214
     * ```
215
     */
216
    @ContentChild(IgxSelectHeaderDirective, { read: TemplateRef, static: false })
UNCOV
217
    public headerTemplate: TemplateRef<any> = null;
×
218

219
    /**
220
     * The custom template, if any, that should be used when rendering the FOOTER for the select items list
221
     *
222
     * ```typescript
223
     * // Set in typescript
224
     * const myCustomTemplate: TemplateRef<any> = myComponent.customTemplate;
225
     * myComponent.select.footerTemplate = myCustomTemplate;
226
     * ```
227
     * ```html
228
     * <!-- Set in markup -->
229
     *  <igx-select #select>
230
     *      ...
231
     *      <ng-template igxSelectFooter>
232
     *          <div class="select__footer">
233
     *              This is a custom footer
234
     *          </div>
235
     *      </ng-template>
236
     *  </igx-select>
237
     * ```
238
     */
239
    @ContentChild(IgxSelectFooterDirective, { read: TemplateRef, static: false })
UNCOV
240
    public footerTemplate: TemplateRef<any> = null;
×
241

242
    @ContentChild(IgxHintDirective, { read: ElementRef }) private hintElement: ElementRef;
243

244
    /** @hidden @internal */
245
    public override width: string;
246

247
    /** @hidden @internal do not use the drop-down container class */
UNCOV
248
    public override cssClass = false;
×
249

250
    /** @hidden @internal */
UNCOV
251
    public override allowItemsFocus = false;
×
252

253
    /** @hidden @internal */
254
    public override height: string;
255

UNCOV
256
    private ngControl: NgControl = null;
×
257
    private _overlayDefaults: OverlaySettings;
258
    private _value: any;
UNCOV
259
    private _type = null;
×
260

261
    /**
262
     * Gets/Sets the component value.
263
     *
264
     * ```typescript
265
     * // get
266
     * let selectValue = this.select.value;
267
     * ```
268
     *
269
     * ```typescript
270
     * // set
271
     * this.select.value = 'London';
272
     * ```
273
     * ```html
274
     * <igx-select [value]="value"></igx-select>
275
     * ```
276
     */
277
    @Input()
278
    public get value(): any {
UNCOV
279
        return this._value;
×
280
    }
281
    public set value(v: any) {
UNCOV
282
        if (this._value === v) {
×
UNCOV
283
            return;
×
284
        }
UNCOV
285
        this._value = v;
×
UNCOV
286
        this.setSelection(this.items.find(x => x.value === this.value));
×
287
    }
288

289
    /**
290
     * Sets how the select will be styled.
291
     * The allowed values are `line`, `box` and `border`. The input-group default is `line`.
292
     * ```html
293
     * <igx-select [type]="'box'"></igx-select>
294
     * ```
295
     */
296
    @Input()
297
    public get type(): IgxInputGroupType {
UNCOV
298
        return this._type || this._inputGroupType || 'line';
×
299
    }
300

301
    public set type(val: IgxInputGroupType) {
UNCOV
302
        this._type = val;
×
303
    }
304

305
    /** @hidden @internal */
306
    public get selectionValue() {
UNCOV
307
        const selectedItem = this.selectedItem;
×
UNCOV
308
        return selectedItem ? selectedItem.itemText : '';
×
309
    }
310

311
    /** @hidden @internal */
312
    public override get selectedItem(): IgxSelectItemComponent {
UNCOV
313
        return this.selection.first_item(this.id);
×
314
    }
315

UNCOV
316
    private _onChangeCallback: (_: any) => void = noop;
×
UNCOV
317
    private _onTouchedCallback: () => void = noop;
×
318

319
    //#region ControlValueAccessor
320

321
    /** @hidden @internal */
UNCOV
322
    public writeValue = (value: any) => {
×
UNCOV
323
        this.value = value;
×
324
    };
325

326
    /** @hidden @internal */
327
    public registerOnChange(fn: any): void {
UNCOV
328
        this._onChangeCallback = fn;
×
329
    }
330

331
    /** @hidden @internal */
332
    public registerOnTouched(fn: any): void {
UNCOV
333
        this._onTouchedCallback = fn;
×
334
    }
335

336
    /** @hidden @internal */
337
    public setDisabledState(isDisabled: boolean): void {
UNCOV
338
        this.disabled = isDisabled;
×
339
    }
340
    //#endregion
341

342
    /** @hidden @internal */
343
    public getEditElement(): HTMLInputElement {
UNCOV
344
        return this.input.nativeElement;
×
345
    }
346

347
    /** @hidden @internal */
348
    public override selectItem(newSelection: IgxDropDownItemBaseDirective, event?) {
UNCOV
349
        const oldSelection = this.selectedItem ?? <IgxDropDownItemBaseDirective>{};
×
350

UNCOV
351
        if (newSelection === null || newSelection.disabled || newSelection.isHeader) {
×
UNCOV
352
            return;
×
353
        }
354

UNCOV
355
        if (newSelection === oldSelection) {
×
UNCOV
356
            this.toggleDirective.close();
×
UNCOV
357
            return;
×
358
        }
359

UNCOV
360
        const args: ISelectionEventArgs = { oldSelection, newSelection, cancel: false, owner: this };
×
UNCOV
361
        this.selectionChanging.emit(args);
×
362

UNCOV
363
        if (args.cancel) {
×
UNCOV
364
            return;
×
365
        }
366

UNCOV
367
        this.setSelection(newSelection);
×
UNCOV
368
        this._value = newSelection.value;
×
369

UNCOV
370
        if (event) {
×
UNCOV
371
            this.toggleDirective.close();
×
372
        }
373

UNCOV
374
        this.cdr.detectChanges();
×
UNCOV
375
        this._onChangeCallback(this.value);
×
376
    }
377

378
    /** @hidden @internal */
379
    public getFirstItemElement(): HTMLElement {
UNCOV
380
        return this.children.first.element.nativeElement;
×
381
    }
382

383
    /**
384
     * Opens the select
385
     *
386
     * ```typescript
387
     * this.select.open();
388
     * ```
389
     */
390
    public override open(overlaySettings?: OverlaySettings) {
UNCOV
391
        if (this.disabled || this.items.length === 0) {
×
UNCOV
392
            return;
×
393
        }
UNCOV
394
        if (!this.selectedItem) {
×
UNCOV
395
            this.navigateFirst();
×
396
        }
397

UNCOV
398
        super.open(Object.assign({}, this._overlayDefaults, this.overlaySettings, overlaySettings));
×
399
    }
400

401
    public inputGroupClick(event: MouseEvent, overlaySettings?: OverlaySettings) {
UNCOV
402
        const targetElement = event.target as HTMLElement;
×
403

UNCOV
404
        if (this.hintElement && targetElement.contains(this.hintElement.nativeElement)) {
×
405
            return;
×
406
        }
UNCOV
407
        this.toggle(Object.assign({}, this._overlayDefaults, this.overlaySettings, overlaySettings));
×
408
    }
409

410
    /** @hidden @internal */
411
    public ngAfterContentInit() {
UNCOV
412
        this._overlayDefaults = {
×
413
            target: this.getEditElement(),
414
            modal: false,
415
            positionStrategy: new SelectPositioningStrategy(this),
416
            scrollStrategy: new AbsoluteScrollStrategy(),
417
            excludeFromOutsideClick: [this.inputGroup.element.nativeElement as HTMLElement]
418
        };
UNCOV
419
        const changes$ = this.children.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
420
            this.setSelection(this.items.find(x => x.value === this.value));
×
UNCOV
421
            this.cdr.detectChanges();
×
422
        });
UNCOV
423
        Promise.resolve().then(() => {
×
UNCOV
424
            if (!changes$.closed) {
×
UNCOV
425
                this.children.notifyOnChanges();
×
426
            }
427
        });
428
    }
429

430
    /**
431
     * Event handlers
432
     *
433
     * @hidden @internal
434
     */
435
    public handleOpening(e: ToggleViewCancelableEventArgs) {
UNCOV
436
        const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
×
UNCOV
437
        this.opening.emit(args);
×
438

UNCOV
439
        e.cancel = args.cancel;
×
UNCOV
440
        if (args.cancel) {
×
441
            return;
×
442
        }
443
    }
444

445
    /** @hidden @internal */
446
    public override onToggleContentAppended(event: ToggleViewEventArgs) {
UNCOV
447
        const info = this.overlayService.getOverlayById(event.id);
×
UNCOV
448
        if (info?.settings?.positionStrategy instanceof SelectPositioningStrategy) {
×
449
            return;
×
450
        }
UNCOV
451
        super.onToggleContentAppended(event);
×
452
    }
453

454
    /** @hidden @internal */
455
    public handleOpened() {
UNCOV
456
        this.updateItemFocus();
×
UNCOV
457
        this.opened.emit({ owner: this });
×
458
    }
459

460
    /** @hidden @internal */
461
    public handleClosing(e: ToggleViewCancelableEventArgs) {
UNCOV
462
        const args: IBaseCancelableBrowserEventArgs = { owner: this, event: e.event, cancel: e.cancel };
×
UNCOV
463
        this.closing.emit(args);
×
UNCOV
464
        e.cancel = args.cancel;
×
465
    }
466

467
    /** @hidden @internal */
468
    public handleClosed() {
UNCOV
469
        this.focusItem(false);
×
UNCOV
470
        this.closed.emit({ owner: this });
×
471
    }
472

473
    /** @hidden @internal */
474
    public onBlur(): void {
UNCOV
475
        this._onTouchedCallback();
×
UNCOV
476
        if (this.ngControl && this.ngControl.invalid) {
×
UNCOV
477
            this.input.valid = IgxInputState.INVALID;
×
478
        } else {
UNCOV
479
            this.input.valid = IgxInputState.INITIAL;
×
480
        }
481
    }
482

483
    /** @hidden @internal */
484
    public onFocus(): void {
UNCOV
485
        this._onTouchedCallback();
×
486
    }
487

488
    /**
489
     * @hidden @internal
490
     */
491
    public override ngOnInit() {
UNCOV
492
        this.ngControl = this._injector.get<NgControl>(NgControl, null);
×
493
    }
494

495
    /**
496
     * @hidden @internal
497
     */
498
    public override ngAfterViewInit() {
UNCOV
499
        super.ngAfterViewInit();
×
500

UNCOV
501
        if (this.ngControl) {
×
UNCOV
502
            this.ngControl.statusChanges.pipe(takeUntil(this.destroy$)).subscribe(this.onStatusChanged.bind(this));
×
UNCOV
503
            this.manageRequiredAsterisk();
×
504
        }
505

UNCOV
506
        this.cdr.detectChanges();
×
507
    }
508

509
    /** @hidden @internal */
510
    public ngAfterContentChecked() {
UNCOV
511
        if (this.inputGroup && this.prefixes?.length > 0) {
×
UNCOV
512
            this.inputGroup.prefixes = this.prefixes;
×
513
        }
514

UNCOV
515
        if (this.inputGroup) {
×
UNCOV
516
            const suffixesArray = this.suffixes?.toArray() ?? [];
×
UNCOV
517
            const internalSuffixesArray = this.internalSuffixes?.toArray() ?? [];
×
UNCOV
518
            const mergedSuffixes = new QueryList<IgxSuffixDirective>();
×
UNCOV
519
            mergedSuffixes.reset([
×
520
                ...suffixesArray,
521
                ...internalSuffixesArray
522
            ]);
UNCOV
523
            this.inputGroup.suffixes = mergedSuffixes;
×
524
        }
525
    }
526

527
    /** @hidden @internal */
528
    public get toggleIcon(): string {
UNCOV
529
        return this.collapsed ? 'input_expand' : 'input_collapse';
×
530
    }
531

532
    /**
533
     * @hidden @internal
534
     * Prevent input blur - closing the items container on Header/Footer Template click.
535
     */
536
    public mousedownHandler(event) {
537
        event.preventDefault();
×
538
    }
539

540
    protected onStatusChanged() {
UNCOV
541
        this.manageRequiredAsterisk();
×
UNCOV
542
        if (this.ngControl && !this.disabled && this.isTouchedOrDirty) {
×
UNCOV
543
            if (this.hasValidators && this.inputGroup.isFocused) {
×
UNCOV
544
                this.input.valid = this.ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID;
×
545
            } else {
546
                // B.P. 18 May 2021: IgxDatePicker does not reset its state upon resetForm #9526
UNCOV
547
                this.input.valid = this.ngControl.valid ? IgxInputState.INITIAL : IgxInputState.INVALID;
×
548
            }
549
        } else {
UNCOV
550
            this.input.valid = IgxInputState.INITIAL;
×
551
        }
552
    }
553

554
    private get isTouchedOrDirty(): boolean {
UNCOV
555
        return (this.ngControl.control.touched || this.ngControl.control.dirty);
×
556
    }
557

558
    private get hasValidators(): boolean {
UNCOV
559
        return (!!this.ngControl.control.validator || !!this.ngControl.control.asyncValidator);
×
560
    }
561

562
    protected override navigate(direction: Navigate, currentIndex?: number) {
UNCOV
563
        if (this.collapsed && this.selectedItem) {
×
UNCOV
564
            this.navigateItem(this.selectedItem.itemIndex);
×
565
        }
UNCOV
566
        super.navigate(direction, currentIndex);
×
567
    }
568

569
    protected manageRequiredAsterisk(): void {
UNCOV
570
        const hasRequiredHTMLAttribute = this.elementRef.nativeElement.hasAttribute('required');
×
UNCOV
571
        let isRequired = false;
×
572

UNCOV
573
        if (this.ngControl && this.ngControl.control.validator) {
×
UNCOV
574
            const error = this.ngControl.control.validator({} as AbstractControl);
×
UNCOV
575
            isRequired = !!(error && error.required);
×
576
        }
577

UNCOV
578
        this.inputGroup.isRequired = isRequired;
×
579

UNCOV
580
        if (this.input?.nativeElement) {
×
UNCOV
581
            this.input.nativeElement.setAttribute('aria-required', isRequired.toString());
×
582
        }
583

584
        // Handle validator removal case
UNCOV
585
        if (!isRequired && !hasRequiredHTMLAttribute) {
×
UNCOV
586
            this.input.valid = IgxInputState.INITIAL;
×
587
        }
588

UNCOV
589
        this.cdr.markForCheck();
×
590
    }
591

592
    private setSelection(item: IgxDropDownItemBaseDirective) {
UNCOV
593
        if (item && item.value !== undefined && item.value !== null) {
×
UNCOV
594
            this.selection.set(this.id, new Set([item]));
×
595
        } else {
UNCOV
596
            this.selection.clear(this.id);
×
597
        }
598
    }
599
}
600

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