Skip to content

Commit cdb83c0

Browse files
committed
refactor(radio-group): remove lifecycle methods
1 parent 0b6e298 commit cdb83c0

File tree

2 files changed

+94
-108
lines changed

2 files changed

+94
-108
lines changed

ROADMAP.md

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
# Current Milestone
44

5-
## Milestone 38, version 20.0 (Due by Jun, 2025)
6-
1. Support of Angular 20.0
5+
## Milestone 37, version 19.2 (Due by Mar, 2025)
6+
7+
1. Tile Manager - layout component [#239](https://github.com/IgniteUI/igniteui-angular/issues/239)
8+
2. SSR Grid Improvements - [15202](https://github.com/IgniteUI/igniteui-angular/issues/15202)
79

810
## Going down the road
911

@@ -13,12 +15,7 @@
1315

1416
# Previous Milestone
1517

16-
## Milestone 37, version 19.2 (Released Apr 16th, 2025), [Release Blog 19.2](https://www.infragistics.com/blogs/ignite-ui-for-angular-19-2/)
17-
18-
1. Tile Manager - layout component [#239](https://github.com/IgniteUI/igniteui-angular/issues/239)
19-
2. SSR Grid Improvements - [15202](https://github.com/IgniteUI/igniteui-angular/issues/15202)
20-
21-
## Milestone 36, version 19.1 (Released Feb 27th, 2025), [Release Blog 19.1](https://www.infragistics.com/blogs/ignite-ui-for-angular-19-1/)
18+
## Milestone 36, version 19.1 (Released Feb 27th, 2025)
2219

2320
1. **[DONE]** Query Builder multi-table query support [#14979](https://github.com/IgniteUI/igniteui-angular/issues/14979)
2421
2. **[DONE]** IgxBannerComponent - Support collapsed input [#14890](https://github.com/IgniteUI/igniteui-angular/issues/14890)
@@ -43,23 +40,23 @@
4340
10. **[DONE]** All palette colors migrated to [CSS relative colors syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors)
4441

4542

46-
## Milestone 33, version 18.1 (Released Jul 22nd, 2024), [Release Blog 24.1](https://www.infragistics.com/blogs/ignite-ui-24-1/)
43+
## Milestone 33, version 18.1 (Released Jul 22nd, 2024)
4744

4845
1. **[DONE]** Horizontal row dimension expansion for Pivot Grid [#14270](https://github.com/IgniteUI/igniteui-angular/issues/14270)
4946
2. **[DONE]** Headers focus and keyboard navigation for Pivot Grid Row Dimension Headers [#14249](https://github.com/IgniteUI/igniteui-angular/issues/14249)
5047
3. **[DONE]** Grid Toolbar refactoring [#8055](https://github.com/IgniteUI/igniteui-angular/issues/8055)
5148
4. **[DONE]** Translation for "Wk" for week numbers in calendars [#14423](https://github.com/IgniteUI/igniteui-angular/issues/14423)
5249
5. **[DONE]** Provide a way to replace part of (or all) icons used by components with icons from other icon sets [#13987](https://github.com/IgniteUI/igniteui-angular/issues/13987)
5350

54-
## Milestone 32, version 18.0 (Released Jun 07th, 2024), [Release Blog 24.1](https://www.infragistics.com/blogs/ignite-ui-24-1/)
51+
## Milestone 32, version 18.0 (Released Jun 07th, 2024)
5552

5653
1. **[DONE]** Support of Angular 18
5754
2. **[DONE]** Expose a header template for the chip area of the headers of the rows dimension in the pivot grid [#14016](https://github.com/IgniteUI/igniteui-angular/issues/14016)
5855
3. **[DONE]** Expose headers for row dimensions to be visible in excel when exporting a pivot grid [#14017](https://github.com/IgniteUI/igniteui-angular/issues/14017)
5956
4. **[DONE]** igxToggle: setOffset function increments the offset instead of setting a specific value [#14174](https://github.com/IgniteUI/igniteui-angular/issues/14174)
6057
5. **[DONE]** BREAKING CHANGE: Depricate of displayDensity property [Update Guide](https://www.infragistics.com/products/ignite-ui-angular/angular/components/general/update-guide#from-172x-to-180x)
6158

62-
## Milestone 31, version 17.2 (Released Apr 29th, 2024), [Release Blog 24.1](https://www.infragistics.com/blogs/ignite-ui-24-1/)
59+
## Milestone 31, version 17.2 (Released Apr 29th, 2024)
6360

6461
1. **[DONE]** Update calendar theme to match material [#10400](https://github.com/IgniteUI/igniteui-angular/issues/10400)
6562
2. **[DONE]** Calendar: Default date formatting in the header [#13811](https://github.com/IgniteUI/igniteui-angular/issues/13811)
@@ -68,7 +65,7 @@
6865
5. **[DONE]** Date/datetime/time editors should accept the same formats that the angular pipes accept [#14011](https://github.com/IgniteUI/igniteui-angular/issues/14011)
6966

7067

71-
## Milestone 30, version 17.1 (Released Feb 26th, 2024), [Release Blog 24.1](https://www.infragistics.com/blogs/ignite-ui-24-1/)
68+
## Milestone 30, version 17.1 (Released Feb 26th, 2024)
7269

7370
1. **[DONE]** Improve package tree-shaking [#13562](https://github.com/IgniteUI/igniteui-angular/issues/13562)
7471
2. **[DONE]** Deprecate rowID and rowData in interfaces [#10617](https://github.com/IgniteUI/igniteui-angular/issues/10617)

projects/igniteui-angular/src/lib/directives/radio/radio-group.directive.ts

Lines changed: 85 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import {
2-
AfterContentInit,
3-
AfterViewInit,
42
ChangeDetectorRef,
5-
ContentChildren, Directive, DoCheck, EventEmitter, HostBinding, HostListener, Input, OnDestroy, Optional, Output, QueryList, Self, booleanAttribute,
6-
contentChildren
3+
Directive, DoCheck, EventEmitter, HostBinding, HostListener, Input, OnDestroy, Optional, Output, Self, booleanAttribute,
4+
contentChildren,
5+
effect,
6+
signal
77
} from '@angular/core';
88
import { ControlValueAccessor, NgControl, Validators } from '@angular/forms';
9-
import { fromEvent, noop, Subject } from 'rxjs';
10-
import { startWith, takeUntil } from 'rxjs/operators';
9+
import { fromEvent, noop, Subject, takeUntil } from 'rxjs';
1110
import { mkenum } from '../../core/utils';
1211
import { IgxRadioComponent } from '../../radio/radio.component';
1312
import { IgxDirectionality } from '../../services/direction/directionality';
@@ -52,7 +51,7 @@ let nextId = 0;
5251
selector: 'igx-radio-group, [igxRadioGroup]',
5352
standalone: true
5453
})
55-
export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit, ControlValueAccessor, OnDestroy, DoCheck {
54+
export class IgxRadioGroupDirective implements ControlValueAccessor, OnDestroy, DoCheck {
5655
/**
5756
* Returns reference to the child radio buttons.
5857
*
@@ -61,9 +60,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
6160
* let radioButtons = this.radioGroup.radioButtons;
6261
* ```
6362
*/
64-
@ContentChildren(IgxRadioComponent, { descendants: true }) public radioButtons: QueryList<IgxRadioComponent>;
65-
66-
public RadioBtnSignal = contentChildren(IgxRadioComponent, { descendants: true });
63+
public radioButtons = contentChildren(IgxRadioComponent, { descendants: true });
6764

6865
/**
6966
* Sets/gets the `value` attribute.
@@ -207,7 +204,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
207204
*/
208205
@HostBinding('class.igx-radio-group--before')
209206
protected get labelBefore() {
210-
return this.RadioBtnSignal().some((radio) => radio.labelPosition === 'before');
207+
return this.radioButtons().some((radio) => radio.labelPosition === 'before');
211208
}
212209

213210
/**
@@ -219,7 +216,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
219216
*/
220217
@HostBinding('class.igx-radio-group--disabled')
221218
protected get disabled() {
222-
return this.RadioBtnSignal().every((radio) => radio.disabled);
219+
return this.radioButtons().every((radio) => radio.disabled);
223220
}
224221

225222
@HostListener('click', ['$event'])
@@ -234,7 +231,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
234231
@HostListener('keydown', ['$event'])
235232
protected handleKeyDown(event: KeyboardEvent) {
236233
const { key } = event;
237-
const buttons = this.radioButtons.filter(radio => !radio.disabled);
234+
const buttons = this.radioButtons().filter(radio => !radio.disabled);
238235
const checked = buttons.find((radio) => radio.checked);
239236

240237
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(key)) {
@@ -331,7 +328,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
331328
* @hidden
332329
* @internal
333330
*/
334-
private _isInitialized = false;
331+
private _isInitialized = signal(false);
335332
/**
336333
* @hidden
337334
* @internal
@@ -353,68 +350,13 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
353350
*/
354351
private queryChange$ = new Subject<void>();
355352

356-
/**
357-
* @hidden
358-
* @internal
359-
*/
360-
public ngAfterContentInit() {
361-
// The initial value can possibly be set by NgModel and it is possible that
362-
// the OnInit of the NgModel occurs after the OnInit of this class.
363-
this._isInitialized = true;
364-
365-
this.radioButtons.changes.pipe(startWith(0), takeUntil(this.destroy$)).subscribe(() => {
366-
this.queryChange$.next();
367-
setTimeout(() => this._initRadioButtons());
368-
});
369-
370-
371-
if (this.ngControl) {
372-
this.radioButtons.forEach((button) => {
373-
if (this.ngControl.disabled) {
374-
button.disabled = this.ngControl.disabled;
375-
}
376-
});
377-
}
378-
}
379-
380-
/**
381-
* @hidden
382-
* @internal
383-
*/
384-
public ngAfterViewInit() {
385-
if (this.ngControl) {
386-
this.ngControl.statusChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
387-
this.invalid = false;
388-
});
389-
390-
if (this.ngControl.control.validator || this.ngControl.control.asyncValidator) {
391-
this._required = this.ngControl?.control?.hasValidator(Validators.required);
392-
}
393-
}
394-
395-
if (this.radioButtons) {
396-
this.radioButtons.forEach((button) => {
397-
button.blurRadio
398-
.pipe(takeUntil(this.destroy$))
399-
.subscribe(() => {
400-
this.updateValidityOnBlur()
401-
});
402-
403-
fromEvent(button.nativeElement, 'keyup')
404-
.pipe(takeUntil(this.destroy$))
405-
.subscribe((event: KeyboardEvent) => {
406-
this.updateOnKeyUp(event)
407-
});
408-
});
409-
}
410-
}
411353

412354
/**
413355
* @hidden
414356
* @internal
415357
*/
416358
private updateValidityOnBlur() {
417-
this.radioButtons.forEach((button) => {
359+
this.radioButtons().forEach((button) => {
418360
button.focused = false;
419361

420362
if (button.invalid) {
@@ -428,10 +370,10 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
428370
* @internal
429371
*/
430372
private updateOnKeyUp(event: KeyboardEvent) {
431-
const checked = this.radioButtons.find(x => x.checked);
373+
const checked = this.radioButtons().find(x => x.checked);
432374

433375
if (event.key === "Tab") {
434-
this.radioButtons.forEach((radio) => {
376+
this.radioButtons().forEach((radio) => {
435377
if (radio === checked) {
436378
checked.focused = true;
437379
}
@@ -447,10 +389,10 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
447389
// Needed so that the keyboard navigation of a radio group
448390
// placed inside a dialog works properly
449391
if (this.radioButtons) {
450-
const checked = this.radioButtons.find(x => x.checked);
392+
const checked = this.radioButtons().find(x => x.checked);
451393

452394
if (checked) {
453-
this.radioButtons.forEach((button) => {
395+
this.radioButtons().forEach((button) => {
454396
checked.nativeElement.tabIndex = 0;
455397

456398
if (button !== checked) {
@@ -496,7 +438,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
496438
*/
497439
public registerOnTouched(fn: () => void) {
498440
if (this.radioButtons) {
499-
this.radioButtons.forEach((button) => {
441+
this.radioButtons().forEach((button) => {
500442
button.registerOnTouched(fn);
501443
});
502444
}
@@ -519,39 +461,86 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
519461
if (this.ngControl !== null) {
520462
this.ngControl.valueAccessor = this;
521463
}
464+
465+
effect(() => {
466+
this.initialize();
467+
this.setRadioButtons();
468+
})
522469
}
523470

524471
/**
525472
* @hidden
526473
* @internal
527474
*/
528-
private _initRadioButtons() {
529-
if (this.radioButtons) {
530-
const props = { name: this._name, required: this._required };
531-
this.radioButtons.forEach((button) => {
532-
Object.assign(button, props);
533-
534-
if (button.value === this._value) {
535-
button.checked = true;
536-
this._selected = button;
537-
this.cdr.markForCheck();
538-
}
475+
private initialize() {
476+
// The initial value can possibly be set by NgModel and it is possible that
477+
// the OnInit of the NgModel occurs after the OnInit of this class.
478+
this._isInitialized.set(true);
479+
480+
if (this.ngControl) {
481+
this.ngControl.statusChanges
482+
.pipe(takeUntil(this.destroy$))
483+
.subscribe(() => {
484+
this.invalid = false;
485+
});
486+
487+
if (this.ngControl.control.validator || this.ngControl.control.asyncValidator) {
488+
this._required = this.ngControl?.control?.hasValidator(Validators.required);
489+
}
539490

540-
button.change.pipe(
541-
takeUntil(button.destroy$),
542-
takeUntil(this.destroy$),
543-
takeUntil(this.queryChange$)
544-
).subscribe((ev) => this._selectedRadioButtonChanged(ev));
491+
this.radioButtons().forEach((button) => {
492+
if (this.ngControl.disabled) {
493+
button.disabled = this.ngControl.disabled;
494+
}
545495
});
546496
}
547497
}
548498

499+
/**
500+
* @hidden
501+
* @internal
502+
*/
503+
private setRadioButtons() {
504+
this.radioButtons().forEach((button) => {
505+
button.name = this._name;
506+
button.required = this._required;
507+
508+
if (button.value === this._value) {
509+
button.checked = true;
510+
this._selected = button;
511+
this.cdr.markForCheck();
512+
}
513+
514+
this._setRadioButtonEvents(button);
515+
});
516+
}
517+
518+
/**
519+
* @hidden
520+
* @internal
521+
*/
522+
private _setRadioButtonEvents(button: any) {
523+
button.change.pipe(
524+
takeUntil(button.destroy$),
525+
takeUntil(this.destroy$),
526+
takeUntil(this.queryChange$)
527+
).subscribe((ev) => this._selectedRadioButtonChanged(ev));
528+
529+
button.blurRadio
530+
.pipe(takeUntil(this.destroy$))
531+
.subscribe(() => this.updateValidityOnBlur());
532+
533+
fromEvent(button.nativeElement, 'keyup')
534+
.pipe(takeUntil(this.destroy$))
535+
.subscribe((event: KeyboardEvent) => this.updateOnKeyUp(event));
536+
}
537+
549538
/**
550539
* @hidden
551540
* @internal
552541
*/
553542
private _selectedRadioButtonChanged(args: IChangeCheckboxEventArgs) {
554-
this.radioButtons.forEach((button) => {
543+
this.radioButtons().forEach((button) => {
555544
button.checked = button.id === args.owner.id;
556545
if (button.checked && button.ngControl) {
557546
this.invalid = button.ngControl.invalid;
@@ -575,7 +564,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
575564
*/
576565
private _setRadioButtonNames() {
577566
if (this.radioButtons) {
578-
this.radioButtons.forEach((button) => {
567+
this.radioButtons().forEach((button) => {
579568
button.name = this._name;
580569
});
581570
}
@@ -587,7 +576,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
587576
*/
588577
private _selectRadioButton() {
589578
if (this.radioButtons) {
590-
this.radioButtons.forEach((button) => {
579+
this.radioButtons().forEach((button) => {
591580
if (this._value === null) {
592581
// no value - uncheck all radio buttons
593582
if (button.checked) {
@@ -620,7 +609,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
620609
*/
621610
private _setRadioButtonsRequired() {
622611
if (this.radioButtons) {
623-
this.radioButtons.forEach((button) => {
612+
this.radioButtons().forEach((button) => {
624613
button.required = this._required;
625614
});
626615
}
@@ -632,7 +621,7 @@ export class IgxRadioGroupDirective implements AfterContentInit, AfterViewInit,
632621
*/
633622
private _setRadioButtonsInvalid() {
634623
if (this.radioButtons) {
635-
this.radioButtons.forEach((button) => {
624+
this.radioButtons().forEach((button) => {
636625
button.invalid = this._invalid;
637626
});
638627
}

0 commit comments

Comments
 (0)