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

IgniteUI / igniteui-angular / 6585881624

20 Oct 2023 09:23AM UTC coverage: 92.206% (+0.003%) from 92.203%
6585881624

push

github

web-flow
Merge pull request #13560 from IgniteUI/chore-readme-milestones-17-oct-23

chore(*): update-readme-roadmap-17-oct/23

15356 of 18052 branches covered (0.0%)

26877 of 29149 relevant lines covered (92.21%)

29682.24 hits per line

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

90.09
/projects/igniteui-angular/src/lib/buttonGroup/buttonGroup.component.ts
1
import { NgFor, NgIf } from '@angular/common';
2
import {
3
    AfterContentInit,
4
    AfterViewInit,
5
    Component,
6
    ContentChildren,
7
    ChangeDetectorRef,
8
    EventEmitter,
9
    HostBinding,
10
    Inject,
11
    Input,
12
    Output,
13
    Optional,
14
    QueryList,
15
    Renderer2,
2✔
16
    ViewChildren,
17
    OnDestroy,
18
    ElementRef
19
} from '@angular/core';
2✔
20
import { Subject } from 'rxjs';
21
import { IgxButtonDirective } from '../directives/button/button.directive';
22
import { IgxRippleDirective } from '../directives/ripple/ripple.directive';
23

24
import { takeUntil } from 'rxjs/operators';
25
import { DisplayDensityBase, DisplayDensityToken, IDisplayDensityOptions } from '../core/density';
26
import { CancelableEventArgs, IBaseEventArgs } from '../core/utils';
27
import { mkenum } from '../core/utils';
28
import { IgxIconComponent } from '../icon/icon.component';
29

30
/**
31
 * Determines the Button Group alignment
32
 */
33
export const ButtonGroupAlignment = mkenum({
34
    horizontal: 'horizontal',
35
    vertical: 'vertical'
36
});
37
export type ButtonGroupAlignment = typeof ButtonGroupAlignment[keyof typeof ButtonGroupAlignment];
38

39
let NEXT_ID = 0;
40

2✔
41
/**
42
 * **Ignite UI for Angular Button Group** -
43
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/buttongroup.html)
44
 *
45
 * The Ignite UI Button Group displays a group of buttons either vertically or horizontally. The group supports
794✔
46
 * single, multi and singleRequired selection.
47
 *
48
 * Example:
4!
49
 * ```html
50
 * <igx-buttongroup selectionMode="multi" [values]="fontOptions">
51
 * </igx-buttongroup>
52
 * ```
53
 * The `fontOptions` value shown above is defined as:
54
 * ```typescript
55
 * this.fontOptions = [
56
 *   { icon: 'format_bold', selected: false },
57
 *   { icon: 'format_italic', selected: false },
58
 *   { icon: 'format_underlined', selected: false }];
59
 * ```
60
 */
61
@Component({
234✔
62
    selector: 'igx-buttongroup',
63
    templateUrl: 'buttongroup-content.component.html',
64
    standalone: true,
×
65
    imports: [NgFor, IgxButtonDirective, IgxRippleDirective, NgIf, IgxIconComponent]
×
66
})
67
export class IgxButtonGroupComponent extends DisplayDensityBase implements AfterContentInit, AfterViewInit, OnDestroy {
68
    /**
×
69
     * A collection containing all buttons inside the button group.
70
     */
71
    public get buttons(): IgxButtonDirective[] {
72
        return [...this.viewButtons.toArray(), ...this.templateButtons.toArray()];
×
73
    }
×
74

75
    /**
76
     * An @Input property that sets the value of the `id` attribute. If not set it will be automatically generated.
×
77
     * ```html
78
     *  <igx-buttongroup [id]="'igx-dialog-56'" [selectionMode]="'multi'" [values]="alignOptions">
79
     * ```
80
     */
172✔
81
    @HostBinding('attr.id')
82
    @Input()
83
    public id = `igx-buttongroup-${NEXT_ID++}`;
12✔
84

4✔
85
    /**
13✔
86
     * @hidden
87
     */
4✔
88
    @HostBinding('style.zIndex')
89
    public zIndex = 0;
90

8✔
91
    /**
92
     * Allows you to set a style using the `itemContentCssClass` input.
93
     * The value should be the CSS class name that will be applied to the button group.
94
     * ```typescript
445✔
95
     * public style1 = "styleClass";
96
     *  //..
97
     * ```
×
98
     *  ```html
×
99
     * <igx-buttongroup [itemContentCssClass]="style1" [selectionMode]="'multi'" [values]="alignOptions">
×
100
     * ```
×
101
     */
102
    @Input()
103
    public set itemContentCssClass(value: string) {
104
        this._itemContentCssClass = value || this._itemContentCssClass;
105
    }
7✔
106

107
    /**
108
     * Returns the CSS class of the item content of the `IgxButtonGroup`.
109
     * ```typescript
110
     *  @ViewChild("MyChild")
111
     * public buttonG: IgxButtonGroupComponent;
112
     * ngAfterViewInit(){
113
     *    let buttonSelect = this.buttonG.itemContentCssClass;
114
     * }
115
     * ```
116
     */
117
    public get itemContentCssClass(): string {
118
        return this._itemContentCssClass;
2✔
119
    }
120

121
    /**
122
     * @deprecated in version 16.1.0. Set/Use selectionMode property instead.
123
     * 
124
     * Enables selecting multiple buttons. By default, multi-selection is false.
125
     */
126
    @Input()
127
    public get multiSelection() {
128
        if (this.selectionMode === 'multi') {
129
            return true;
130
        } else {
131
            return false;
132
        }
133
    }
134
    public set multiSelection(selectionMode: boolean) {
135
        if (selectionMode) {
136
            this.selectionMode = 'multi';
604✔
137
        } else {
138
            this.selectionMode = 'single';
139
        }
91✔
140
    }
91✔
141

91✔
142
    /**
91✔
143
     * An @Input property that get/set the selection mode 'single', 'singleRequired' or 'multi' of the buttons. By default, the selection mode is 'single'.
91✔
144
     * ```html
91✔
145
     * <igx-buttongroup [selectionMode]="'multi'" [alignment]="alignment"></igx-buttongroup>
91✔
146
     * ```
91✔
147
     */
91✔
148
    @Input()
149
    public get selectionMode() {
150
        return this._selectionMode;
151
    }
91✔
152
    public set selectionMode(selectionMode: 'single' | 'singleRequired' | 'multi') {
91✔
153
        if (this.viewButtons && selectionMode !== this._selectionMode) {
91✔
154
            this.buttons.forEach((b,i) => {
91✔
155
                this.deselectButton(i);
91✔
156
            });
91✔
157
            this._selectionMode = selectionMode;
158
        } else {
159
            this._selectionMode = selectionMode;
160
        }
161
    }
162

163
    /**
164
     * An @Input property that allows setting the buttons in the button group.
165
     * ```typescript
166
     *  public ngOnInit() {
167
     *      this.cities = [
168
     *        new Button({
169
     *          label: "Sofia"
121✔
170
     *      }),
171
     *        new Button({
172
     *          label: "London"
173
     *      }),
174
     *        new Button({
175
     *          label: "New York",
176
     *          selected: true
177
     *      }),
178
     *        new Button({
179
     *          label: "Tokyo"
180
     *      })
181
     *  ];
182
     *  }
183
     *  //..
184
     * ```
185
     * ```html
57✔
186
     *  <igx-buttongroup [selectionMode]="'single'" [values]="cities"></igx-buttongroup>
2✔
187
     * ```
188
     */
55✔
189
    @Input() public values: any;
55✔
190

191
    /**
192
     * An @Input property that allows you to disable the `igx-buttongroup` component. By default it's false.
193
     * ```html
194
     * <igx-buttongroup [disabled]="true" [selectionMode]="'multi'" [values]="fontOptions"></igx-buttongroup>
195
     * ```
196
     */
114✔
197
    @Input()
114✔
198
    public get disabled(): boolean {
111✔
199
        return this._disabled;
200
    }
114✔
201
    public set disabled(value: boolean) {
114✔
202
        if (this._disabled !== value) {
114✔
203
            this._disabled = value;
114✔
204

29✔
205
            if (this.viewButtons && this.templateButtons) {
206
                this.buttons.forEach((b) => (b.disabled = this._disabled));
207
            }
114✔
208
        }
19✔
209
    }
51✔
210

19✔
211
    /**
212
     * Allows you to set the button group alignment.
213
     * Available options are `ButtonGroupAlignment.horizontal` (default) and `ButtonGroupAlignment.vertical`.
214
     * ```typescript
215
     * public alignment = ButtonGroupAlignment.vertical;
216
     * //..
217
     * ```
218
     * ```html
219
     * <igx-buttongroup [selectionMode]="'single'" [values]="cities" [alignment]="alignment"></igx-buttongroup>
220
     * ```
221
     */
222
    @Input()
223
    public set alignment(value: ButtonGroupAlignment) {
224
        this._isVertical = value === ButtonGroupAlignment.vertical;
225
    }
226
    /**
227
     * Returns the alignment of the `igx-buttongroup`.
228
     * ```typescript
229
     * @ViewChild("MyChild")
139✔
230
     * public buttonG: IgxButtonGroupComponent;
2✔
231
     * ngAfterViewInit(){
232
     *    let buttonAlignment = this.buttonG.alignment;
137✔
233
     * }
137✔
234
     * ```
137✔
235
     */
137✔
236
    public get alignment(): ButtonGroupAlignment {
137✔
237
        return this._isVertical ? ButtonGroupAlignment.vertical : ButtonGroupAlignment.horizontal;
137✔
238
    }
137✔
239

12✔
240
    /**
241
     * An @Ouput property that emits an event when a button is selected.
242
     * ```typescript
243
     * @ViewChild("toast")
244
     * private toast: IgxToastComponent;
245
     * public selectedHandler(buttongroup) {
246
     *     this.toast.open()
91✔
247
     * }
153✔
248
     *  //...
28✔
249
     * ```
250
     * ```html
251
     * <igx-buttongroup #MyChild [selectionMode]="'multi'" (selected)="selectedHandler($event)"></igx-buttongroup>
252
     * <igx-toast #toast>You have made a selection!</igx-toast>
253
     * ```
254
     */
255
    @Output()
256
    public selected = new EventEmitter<IButtonGroupEventArgs>();
93✔
257

258
    /**
93✔
259
     * An @Ouput property that emits an event when a button is deselected.
93✔
260
     * ```typescript
261
     *  @ViewChild("toast")
93✔
262
     *  private toast: IgxToastComponent;
211✔
263
     *  public deselectedHandler(buttongroup){
211✔
264
     *     this.toast.open()
211!
265
     * }
×
266
     *  //...
267
     * ```
211✔
268
     * ```html
53✔
269
     * <igx-buttongroup> #MyChild [selectionMode]="'multi'" (deselected)="deselectedHandler($event)"></igx-buttongroup>
270
     * <igx-toast #toast>You have deselected a button!</igx-toast>
211✔
271
     * ```
211✔
272
     */
273
    @Output()
61✔
274
    public deselected = new EventEmitter<IButtonGroupEventArgs>();
275

276
    @ViewChildren(IgxButtonDirective) private viewButtons: QueryList<IgxButtonDirective>;
93✔
277
    @ContentChildren(IgxButtonDirective) private templateButtons: QueryList<IgxButtonDirective>;
93✔
278

93✔
279
    /**
93✔
280
     * Returns true if the `igx-buttongroup` alignment is vertical.
281
     * Note that in order for the accessor to work correctly the property should be set explicitly.
282
     * ```html
283
     * <igx-buttongroup #MyChild [alignment]="alignment" [values]="alignOptions">
284
     * ```
285
     * ```typescript
91✔
286
     * //...
91✔
287
     * @ViewChild("MyChild")
91✔
288
     * private buttonG: IgxButtonGroupComponent;
91✔
289
     * ngAfterViewInit(){
91✔
290
     *    let orientation = this.buttonG.isVertical;
91✔
291
     * }
292
     * ```
293
     */
294
    public get isVertical(): boolean {
295
        return this._isVertical;
296
    }
39✔
297

39✔
298
    /**
39✔
299
     * @hidden
34✔
300
     */
73✔
301
    public selectedIndexes: number[] = [];
10✔
302

303
    protected buttonClickNotifier$ = new Subject<boolean>();
304
    protected buttonSelectedNotifier$ = new Subject<boolean>();
305
    protected queryListNotifier$ = new Subject<boolean>();
39✔
306

26✔
307
    private _isVertical: boolean;
26✔
308
    private _itemContentCssClass: string;
25✔
309
    private _disabled = false;
310
    private _selectionMode: 'single' | 'singleRequired' | 'multi' = 'single';
311

312
    constructor(
13✔
313
        private _cdr: ChangeDetectorRef,
12✔
314
        private _renderer: Renderer2,
12✔
315
        private _el: ElementRef,
11✔
316
        @Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions
317
    ) {
318
        super(_displayDensityOptions, _el);
319
    }
320

2✔
321
    /**
322
     * Gets the selected button/buttons.
323
     * ```typescript
324
     * @ViewChild("MyChild")
325
     * private buttonG: IgxButtonGroupComponent;
326
     * ngAfterViewInit(){
2✔
327
     *    let selectedButton = this.buttonG.selectedButtons;
328
     * }
329
     * ```
330
     */
331
    public get selectedButtons(): IgxButtonDirective[] {
332
        return this.buttons.filter((_, i) => this.selectedIndexes.indexOf(i) !== -1);
333
    }
334

335
    /**
336
     * Selects a button by its index.
337
     * ```typescript
338
     * @ViewChild("MyChild")
339
     * private buttonG: IgxButtonGroupComponent;
340
     * ngAfterViewInit(){
341
     *    this.buttonG.selectButton(2);
2✔
342
     *    this.cdr.detectChanges();
343
     * }
344
     * ```
345
     *
346
     * @memberOf {@link IgxButtonGroupComponent}
347
     */
348
    public selectButton(index: number) {
349
        if (index >= this.buttons.length || index < 0) {
350
            return;
351
        }
352

353
        const button = this.buttons[index];
354
        button.select();
355
    }
356

357
    /**
358
     * @hidden
359
     * @internal
360
     */
361
    public updateSelected(index: number) {
362
        const button = this.buttons[index];
363

364
        if (this.selectedIndexes.indexOf(index) === -1) {
365
            this.selectedIndexes.push(index);
366
        }
367

368
        this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'true');
369
        this._renderer.addClass(button.nativeElement, 'igx-button-group__item--selected');
370

371
        const indexInViewButtons = this.viewButtons.toArray().indexOf(button);
372
        if (indexInViewButtons !== -1) {
373
            this.values[indexInViewButtons].selected = true;
374
        }
375

376
        // deselect other buttons if selectionMode is not multi
377
        if (this.selectionMode !== 'multi' && this.selectedIndexes.length > 1) {
378
            this.buttons.forEach((_, i) => {
379
                if (i !== index && this.selectedIndexes.indexOf(i) !== -1) {
380
                    this.deselectButton(i);
381
                }
382
            });
383
        }
384
    }
385

386
    /**
387
     * Deselects a button by its index.
388
     * ```typescript
389
     * @ViewChild("MyChild")
390
     * private buttonG: IgxButtonGroupComponent;
391
     * ngAfterViewInit(){
392
     *    this.buttonG.deselectButton(2);
393
     *    this.cdr.detectChanges();
394
     * }
395
     * ```
396
     *
397
     * @memberOf {@link IgxButtonGroupComponent}
398
     */
399
    public deselectButton(index: number) {
400
        if (index >= this.buttons.length || index < 0) {
401
            return;
402
        }
403

404
        const button = this.buttons[index];
405
        this.selectedIndexes.splice(this.selectedIndexes.indexOf(index), 1);
406

407
        this._renderer.setAttribute(button.nativeElement, 'aria-pressed', 'false');
408
        this._renderer.removeClass(button.nativeElement, 'igx-button-group__item--selected');
409
        button.deselect();
410

411
        const indexInViewButtons = this.viewButtons.toArray().indexOf(button);
412
        if (indexInViewButtons !== -1) {
413
            this.values[indexInViewButtons].selected = false;
414
        }
415
    }
416

417
    /**
418
     * @hidden
419
     */
420
    public ngAfterContentInit() {
421
        this.templateButtons.forEach((button) => {
422
            if (!button.initialDensity) {
423
                button.displayDensity = this.displayDensity;
424
            }
425
        });
426
    }
427

428
    /**
429
     * @hidden
430
     */
431
    public ngAfterViewInit() {
432
        const initButtons = () => {
433
            // Cancel any existing buttonClick subscriptions
434
            this.buttonClickNotifier$.next();
435

436
            this.selectedIndexes.splice(0, this.selectedIndexes.length);
437

438
            // initial configuration
439
            this.buttons.forEach((button, index) => {
440
                const buttonElement = button.nativeElement;
441
                this._renderer.addClass(buttonElement, 'igx-button-group__item');
442

443
                if (this.disabled) {
444
                    button.disabled = true;
445
                }
446

447
                if (button.selected) {
448
                    this.updateSelected(index);
449
                }
450

451
                button.buttonClick.pipe(takeUntil(this.buttonClickNotifier$)).subscribe((_) => this._clickHandler(index));
452
                button.buttonSelected
453
                    .pipe(takeUntil(this.buttonSelectedNotifier$))
454
                    .subscribe((_) => this.updateSelected(index));
455
            });
456
        };
457

458
        this.viewButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons());
459
        this.templateButtons.changes.pipe(takeUntil(this.queryListNotifier$)).subscribe(() => initButtons());
460
        initButtons();
461

462
        this._cdr.detectChanges();
463
    }
464

465
    /**
466
     * @hidden
467
     */
468
    public ngOnDestroy() {
469
        this.buttonClickNotifier$.next();
470
        this.buttonClickNotifier$.complete();
471

472
        this.buttonSelectedNotifier$.next();
473
        this.buttonSelectedNotifier$.complete();
474

475
        this.queryListNotifier$.next();
476
        this.queryListNotifier$.complete();
477
    }
478

479
    /**
480
     * @hidden
481
     */
482
    public _clickHandler(index: number) {
483
        const button = this.buttons[index];
484
        const args: IButtonGroupEventArgs = { cancel: false, owner: this, button, index };
485

486
        if (this.selectionMode !== 'multi') {
487
            this.buttons.forEach((b, i) => {
488
                if (i !== index && this.selectedIndexes.indexOf(i) !== -1) {
489
                    this.deselected.emit({ cancel: false, owner: this, button: b, index: i });
490
                }
491
            });
492
        }
493

494
        if (this.selectedIndexes.indexOf(index) === -1) {
495
            this.selected.emit(args);
496
            if(!args.cancel){
497
                this.selectButton(index);
498
            }
499
        } else {
500
            if (this.selectionMode !== 'singleRequired') {
501
                this.deselected.emit(args);
502
                if(!args.cancel){
503
                    this.deselectButton(index);
504
                }
505
            }
506
        }
507
    }
508
}
509

510
export interface IButtonGroupEventArgs extends IBaseEventArgs, CancelableEventArgs {
511
    owner: IgxButtonGroupComponent;
512
    button: IgxButtonDirective;
513
    index: number;
514
}
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