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

IgniteUI / igniteui-angular / 28013325390

23 Jun 2026 08:34AM UTC coverage: 90.139% (-0.02%) from 90.154%
28013325390

Pull #17324

github

web-flow
Merge 690ff31c5 into 01244911c
Pull Request #17324: fix(skills): omit column widths by default in generated grid code

14880 of 17339 branches covered (85.82%)

Branch coverage included in aggregate %.

29947 of 32392 relevant lines covered (92.45%)

34656.81 hits per line

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

90.76
/projects/igniteui-angular/input-group/src/input-group/input-group.component.ts
1
import { NgTemplateOutlet } from '@angular/common';
2
import {
3
  ChangeDetectorRef,
4
  Component,
5
  ContentChild,
6
  ContentChildren,
7
  DestroyRef,
8
  ElementRef,
9
  HostBinding,
10
  HostListener, Input,
11
  QueryList, booleanAttribute,
12
  inject,
13
  DOCUMENT,
14
  AfterContentChecked,
15
  ChangeDetectionStrategy
16
} from '@angular/core';
17
import { IInputResourceStrings, InputResourceStringsEN } from 'igniteui-angular/core';
18
import { PlatformUtil, getComponentTheme } from 'igniteui-angular/core';
19
import { IgxButtonDirective } from 'igniteui-angular/directives';
20
import { IgxHintDirective } from './directives-hint/hint.directive';
21
import {
22
    IgxInputDirective,
23
    IgxInputState
24
} from './directives-input/input.directive';
25
import { IgxPrefixDirective } from './directives-prefix/prefix.directive';
26
import { IgxSuffixDirective } from './directives-suffix/suffix.directive';
27

28
import { IgxInputGroupBase } from './input-group.common';
29
import { IgxInputGroupType, IGX_INPUT_GROUP_TYPE } from './inputGroupType';
30
import { IgxIconComponent } from 'igniteui-angular/icon';
31
import { getCurrentResourceStrings, onResourceChangeHandle } from 'igniteui-angular/core';
32
import { IgxTheme, THEME_TOKEN, ThemeToken } from 'igniteui-angular/core';
33

34
@Component({
35
    selector: 'igx-input-group',
36
    templateUrl: 'input-group.component.html',
37
    providers: [{ provide: IgxInputGroupBase, useExisting: IgxInputGroupComponent }],
38
    changeDetection: ChangeDetectionStrategy.Eager,
39
    imports: [NgTemplateOutlet, IgxPrefixDirective, IgxButtonDirective, IgxSuffixDirective, IgxIconComponent]
40
})
41
export class IgxInputGroupComponent implements IgxInputGroupBase, AfterContentChecked {
3✔
42
    public element = inject<ElementRef<HTMLElement>>(ElementRef);
3,883✔
43
    private _inputGroupType = inject<IgxInputGroupType>(IGX_INPUT_GROUP_TYPE, { optional: true });
3,883✔
44
    private document = inject(DOCUMENT);
3,883✔
45
    private platform = inject(PlatformUtil);
3,883✔
46
    private cdr = inject(ChangeDetectorRef);
3,883✔
47
    private themeToken = inject<ThemeToken>(THEME_TOKEN);
3,883✔
48

49
    /**
50
     * Sets the resource strings.
51
     * By default it uses EN resources.
52
     */
53
    @Input()
54
    public set resourceStrings(value: IInputResourceStrings) {
55
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
×
56
    }
57

58
    /**
59
     * Returns the resource strings.
60
     */
61
    public get resourceStrings(): IInputResourceStrings {
62
        return this._resourceStrings || this._defaultResourceStrings;
105✔
63
    }
64

65
    /**
66
     * Property that enables/disables the auto-generated class of the input group.
67
     * By default applied the class is applied.
68
     * ```typescript
69
     *  @ViewChild("MyInputGroup")
70
     *  public inputGroup: IgxInputGroupComponent;
71
     *  ngAfterViewInit(){
72
     *  this.inputGroup.defaultClass = false;
73
     * ```
74
     * }
75
     */
76
    @HostBinding('class.igx-input-group')
77
    public defaultClass = true;
3,883✔
78

79
    /** @hidden */
80
    @HostBinding('class.igx-input-group--placeholder')
81
    public hasPlaceholder = false;
3,883✔
82

83
    /** @hidden */
84
    @HostBinding('class.igx-input-group--required')
85
    public isRequired = false;
3,883✔
86

87
    /** @hidden */
88
    @HostBinding('class.igx-input-group--focused')
89
    public isFocused = false;
3,883✔
90

91
    /**
92
     * @hidden @internal
93
     * When truthy, disables the input group.
94
     * Controlled by the underlying input.
95
     * ```html
96
     * <igx-input-group [disabled]="true"></igx-input-group>
97
     * ```
98
     */
99
    @HostBinding('class.igx-input-group--disabled')
100
    public disabled = false;
3,883✔
101

102
    /**
103
     * Prevents automatically focusing the input when clicking on other elements in the input group (e.g. prefix or suffix).
104
     *
105
     * @remarks Automatic focus causes software keyboard to show on mobile devices.
106
     *
107
     * @example
108
     * ```html
109
     * <igx-input-group [suppressInputAutofocus]="true"></igx-input-group>
110
     * ```
111
     */
112
    @Input({ transform: booleanAttribute })
113
    public suppressInputAutofocus = false;
3,883✔
114

115
    /** @hidden */
116
    @HostBinding('class.igx-input-group--warning')
117
    public hasWarning = false;
3,883✔
118

119
    /** @hidden */
120
    @ContentChildren(IgxHintDirective, { read: IgxHintDirective })
121
    protected hints: QueryList<IgxHintDirective>;
122

123
    @ContentChildren(IgxPrefixDirective, { read: IgxPrefixDirective, descendants: true })
124
    protected _prefixes: QueryList<IgxPrefixDirective>;
125

126
    @ContentChildren(IgxSuffixDirective, { read: IgxSuffixDirective, descendants: true })
127
    protected _suffixes: QueryList<IgxSuffixDirective>;
128

129
    /** @hidden */
130
    @ContentChild(IgxInputDirective, { read: IgxInputDirective, static: true })
131
    protected input: IgxInputDirective;
132

133
    private _destroyRef = inject(DestroyRef);
3,883✔
134
    private _type: IgxInputGroupType = null;
3,883✔
135
    private _filled = false;
3,883✔
136
    private _theme: IgxTheme;
137
    private _resourceStrings: IInputResourceStrings = null;
3,883✔
138
    private _defaultResourceStrings = getCurrentResourceStrings(InputResourceStringsEN);
3,883✔
139
    private _readOnly: undefined | boolean;
140

141
    /** @hidden @internal */
142
    @HostBinding('class.igx-input-group--readonly')
143
    public get readOnly(): boolean {
144
        return this._readOnly ?? (this.input?.nativeElement.readOnly || false);
66,096✔
145
    }
146

147
    /** @hidden @internal */
148
    public set readOnly(value: boolean) {
149
        this._readOnly = value;
1,724✔
150
    }
151

152
    /** @hidden */
153
    @HostBinding('class.igx-input-group--valid')
154
    public get validClass(): boolean {
155
        return this.input.valid === IgxInputState.VALID;
66,096✔
156
    }
157

158
    /** @hidden */
159
    @HostBinding('class.igx-input-group--invalid')
160
    public get invalidClass(): boolean {
161
        return this.input.valid === IgxInputState.INVALID;
66,096✔
162
    }
163

164
    /** @hidden */
165
    @HostBinding('class.igx-input-group--filled')
166
    public get isFilled() {
167
        return this._filled || (this.input && this.input.value);
66,201✔
168
    }
169

170
    /** @hidden */
171
    @HostBinding('class.igx-input-group--textarea-group')
172
    public get textAreaClass(): boolean {
173
        return this.input.isTextArea;
66,096✔
174
    }
175

176
    /**
177
     * Sets how the input will be styled.
178
     * Allowed values of type input group type.
179
     * ```html
180
     * <igx-input-group [type]="'search'">
181
     * ```
182
     */
183
    @Input()
184
    public set type(value: IgxInputGroupType) {
185
        this._type = value;
3,042✔
186
    }
187

188
    /**
189
     * Returns the type of the input group. How the input is styled.
190
     * The default is `box`.
191
     * ```typescript
192
     * @ViewChild("MyInputGroup")
193
     * public inputGroup: IgxInputGroupComponent;
194
     * ngAfterViewInit(){
195
     *    let inputType = this.inputGroup.type;
196
     * }
197
     * ```
198
     */
199
    public get type() {
200
        return this._type || this._inputGroupType || 'box';
388,681✔
201
    }
202

203
    /**
204
     * Sets the theme of the input.
205
     * Allowed values of type input group theme.
206
     * ```typescript
207
     * @ViewChild("MyInputGroup")
208
     * public inputGroup: IgxInputGroupComponent;
209
     * ngAfterViewInit() {
210
     *  let inputTheme = 'fluent';
211
     * }
212
     */
213
    @Input()
214
    public set theme(value: IgxTheme) {
215
        this._theme = value;
×
216
    }
217

218
    /**
219
     * Returns the theme of the input.
220
     * The returned value is of type input group type.
221
     * ```typescript
222
     * @ViewChild("MyInputGroup")
223
     * public inputGroup: IgxInputGroupComponent;
224
     * ngAfterViewInit() {
225
     *  let inputTheme = this.inputGroup.theme;
226
     * }
227
     */
228
    public get theme(): IgxTheme {
229
        return this._theme;
66,095✔
230
    }
231

232
    constructor() {
233
        this._theme = this.themeToken.theme;
3,883✔
234
        const themeChange = this.themeToken.onChange((theme) => {
3,883✔
235
            if (this._theme !== theme) {
3,883!
236
                this._theme = theme;
×
237
                this.cdr.detectChanges();
×
238
            }
239
        });
240
        this._destroyRef.onDestroy(() => themeChange.unsubscribe());
3,883✔
241
        onResourceChangeHandle(this._destroyRef, () => {
3,883✔
242
            this._defaultResourceStrings = getCurrentResourceStrings(InputResourceStringsEN, false);
536✔
243
        }, this);
244
    }
245

246
    /** @hidden */
247
    @HostListener('click', ['$event'])
248
    public onClick(event: MouseEvent) {
249
        if (
290✔
250
            !this.isFocused &&
660✔
251
            event.target !== this.input.nativeElement &&
252
            !this.suppressInputAutofocus
253
        ) {
254
            this.input.focus();
172✔
255
        }
256
    }
257

258
    /** @hidden */
259
    @HostListener('pointerdown', ['$event'])
260
    public onPointerDown(event: PointerEvent) {
261
        if (this.isFocused && event.target !== this.input.nativeElement) {
19✔
262
            event.preventDefault();
3✔
263
        }
264
    }
265

266
    /** @hidden @internal */
267
    public hintClickHandler(event: MouseEvent) {
268
        event.stopPropagation();
2✔
269
    }
270

271
    /**
272
     * Returns whether the input group has hints.
273
     * ```typescript
274
     * @ViewChild("MyInputGroup")
275
     * public inputGroup: IgxInputGroupComponent;
276
     * ngAfterViewInit(){
277
     *    let inputHints = this.inputGroup.hasHints;
278
     * }
279
     * ```
280
     */
281
    public get hasHints() {
282
        return this.hints.length > 0;
×
283
    }
284

285
    /** @hidden @internal */
286
    @HostBinding('class.igx-input-group--prefixed')
287
    public get hasPrefixes() {
288
        return this._prefixes.length > 0;
66,096✔
289
    }
290

291
    /** @hidden @internal */
292
    public set prefixes(items: QueryList<IgxPrefixDirective>) {
293
        this._prefixes = items;
869✔
294
    }
295

296
    /** @hidden @internal */
297
    @HostBinding('class.igx-input-group--suffixed')
298
    public get hasSuffixes() {
299
        return this._suffixes.length > 0 || this.isFileType && this.isFilled;
66,096✔
300
    }
301

302
    /** @hidden @internal */
303
    public set suffixes(items: QueryList<IgxSuffixDirective>) {
304
        this._suffixes = items;
18,412✔
305
    }
306

307
    /**
308
     * Returns whether the input group has border.
309
     * ```typescript
310
     * @ViewChild("MyInputGroup")
311
     * public inputGroup: IgxInputGroupComponent;
312
     * ngAfterViewInit(){
313
     *    let inputBorder = this.inputGroup.hasBorder;
314
     * }
315
     * ```
316
     */
317
    public get hasBorder() {
318
        return (
66,095✔
319
            (this.type === 'line' || this.type === 'box') &&
187,453✔
320
            this._theme === 'material'
321
        );
322
    }
323

324
    /**
325
     * Returns whether the input group type is line.
326
     * ```typescript
327
     * @ViewChild("MyInputGroup1")
328
     * public inputGroup: IgxInputGroupComponent;
329
     * ngAfterViewInit(){
330
     *    let isTypeLine = this.inputGroup.isTypeLine;
331
     * }
332
     * ```
333
     */
334
    public get isTypeLine(): boolean {
335
        return this.type === 'line' && this._theme === 'material';
10✔
336
    }
337

338
    /**
339
     * Returns whether the input group type is box.
340
     * ```typescript
341
     * @ViewChild("MyInputGroup1")
342
     * public inputGroup: IgxInputGroupComponent;
343
     * ngAfterViewInit(){
344
     *    let isTypeBox = this.inputGroup.isTypeBox;
345
     * }
346
     * ```
347
     */
348
    @HostBinding('class.igx-input-group--box')
349
    public get isTypeBox() {
350
        return this.type === 'box' && this._theme === 'material';
132,201✔
351
    }
352

353
    /** @hidden @internal */
354
    public clearValueHandler() {
355
        this.input.clear();
2✔
356
    }
357

358
    /** @hidden @internal */
359
    @HostBinding('class.igx-input-group--file')
360
    public get isFileType() {
361
        return this.input.type === 'file';
554,170✔
362
    }
363

364
    /** @hidden @internal */
365
    @HostBinding('class.igx-file-input')
366
    public get isFileInput() {
367
        return this.input.type === 'file';
66,096✔
368
    }
369

370
    /** @hidden @internal */
371
    @HostBinding('class.igx-file-input--filled')
372
    public get isFileInputFilled() {
373
        return this.isFileType && this.isFilled;
66,096✔
374
    }
375

376
    /** @hidden @internal */
377
    @HostBinding('class.igx-file-input--focused')
378
    public get isFileInputFocused() {
379
        return this.isFileType && this.isFocused;
66,096✔
380
    }
381

382
    /** @hidden @internal */
383
    @HostBinding('class.igx-file-input--disabled')
384
    public get isFileInputDisabled() {
385
        return this.isFileType && this.disabled;
66,096✔
386
    }
387

388
    /** @hidden @internal */
389
    public get fileNames() {
390
        return this.input.fileNames || this.resourceStrings.igx_input_file_placeholder;
70✔
391
    }
392

393
    /**
394
     * Returns whether the input group type is border.
395
     * ```typescript
396
     * @ViewChild("MyInputGroup1")
397
     * public inputGroup: IgxInputGroupComponent;
398
     * ngAfterViewInit(){
399
     *    let isTypeBorder = this.inputGroup.isTypeBorder;
400
     * }
401
     * ```
402
     */
403
    @HostBinding('class.igx-input-group--border')
404
    public get isTypeBorder() {
405
        return this.type === 'border' && this._theme === 'material';
66,106✔
406
    }
407

408
    /**
409
     * Returns true if the input group theme is Fluent.
410
     * ```typescript
411
     * @ViewChild("MyInputGroup1")
412
     * public inputGroup: IgxInputGroupComponent;
413
     * ngAfterViewInit(){
414
     *    let isTypeFluent = this.inputGroup.isTypeFluent;
415
     * }
416
     * ```
417
     */
418
    @HostBinding('class.igx-input-group--fluent')
419
    public get isTypeFluent() {
420
        return this._theme === 'fluent';
66,096✔
421
    }
422

423
    /**
424
     * Returns true if the input group theme is Bootstrap.
425
     * ```typescript
426
     * @ViewChild("MyInputGroup1")
427
     * public inputGroup: IgxInputGroupComponent;
428
     * ngAfterViewInit(){
429
     *    let isTypeBootstrap = this.inputGroup.isTypeBootstrap;
430
     * }
431
     * ```
432
     */
433
    @HostBinding('class.igx-input-group--bootstrap')
434
    public get isTypeBootstrap() {
435
        return this._theme === 'bootstrap';
66,096✔
436
    }
437

438
    /**
439
     * Returns true if the input group theme is Indigo.
440
     * ```typescript
441
     * @ViewChild("MyInputGroup1")
442
     * public inputGroup: IgxInputGroupComponent;
443
     * ngAfterViewInit(){
444
     *    let isTypeIndigo = this.inputGroup.isTypeIndigo;
445
     * }
446
     * ```
447
     */
448
    @HostBinding('class.igx-input-group--indigo')
449
    public get isTypeIndigo() {
450
        return this._theme === 'indigo';
66,096✔
451
    }
452

453
    /**
454
     * Returns whether the input group type is search.
455
     * ```typescript
456
     * @ViewChild("MyInputGroup1")
457
     * public inputGroup: IgxInputGroupComponent;
458
     * ngAfterViewInit(){
459
     *    let isTypeSearch = this.inputGroup.isTypeSearch;
460
     * }
461
     * ```
462
     */
463
    @HostBinding('class.igx-input-group--search')
464
    public get isTypeSearch() {
465
        if(!this.isFileType && !this.input.isTextArea) {
66,106✔
466
            return this.type === 'search';
65,941✔
467
        }
468
    }
469

470
    /** @hidden */
471
    public get filled() {
472
        return this._filled;
×
473
    }
474

475
    /** @hidden */
476
    public set filled(val) {
477
        this._filled = val;
×
478
    }
479

480
    private setComponentTheme() {
481
        if (!this.themeToken.preferToken) {
40,356✔
482
            const theme = getComponentTheme(this.element.nativeElement);
40,356✔
483

484
            if (theme && theme !== this._theme) {
40,356!
485
                this.theme = theme;
×
486
                this.cdr.markForCheck();
×
487
            }
488
        }
489
    }
490

491
    /** @hidden @internal */
492
    public ngAfterContentChecked() {
493
        this.setComponentTheme();
40,356✔
494
    }
495
}
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