Conversation
Replace the plain backing fields of the shared checkbox/switch/radio base directive with Angular signals, grouped at the top of the class so that each JSDoc block documents the public accessor rather than the backing field. The public API is unchanged: every member keeps its @input()/@output() decorator and plain property shape, so `checkbox.checked = true` and [checked]="x" behave exactly as before. Also: - replace the destroy$/takeUntil cleanup around ngControl.statusChanges with takeUntilDestroyed(); destroy$ was never completed, so it never fired - move the @HostBinding/@HostListener declarations into the decorator's host metadata - expose destroyRef so a parent can scope subscriptions to a single instance Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Switch from ChangeDetectionStrategy.Eager to OnPush, now that the state the component renders is signal-backed and marks the view dirty on every write. Also moves the @HostBinding declarations into the decorator's host metadata. The public API is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Switch from ChangeDetectionStrategy.Eager to OnPush, now that the state the component renders is signal-backed and marks the view dirty on every write. Also moves the @HostBinding declarations into the decorator's host metadata. The public API is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Switch from ChangeDetectionStrategy.Eager to OnPush, now that the state the component renders is signal-backed and marks the view dirty on every write. Also moves the @HostBinding/@HostListener declarations into the decorator's host metadata. The public API is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Convert the ariaChecked and labelClass getters to memoized computed() signals and read the backing signals directly from the templates. Both getters ran once per binding on every dirty change-detection pass, with labelClass allocating a new string each time. As computed() they recompute only when their dependencies change: measured 10 -> 0 recomputations over 10 dirty passes with labelPosition unchanged. The public ariaChecked/labelClass getters are removed. Both were marked @hidden @internal and were referenced only by these three templates. required deliberately stays a getter: it falls back to nativeElement.hasAttribute('required'), which is not part of the reactive graph, so a computed() would go stale and drop the fallback. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_setRadioButtonEvents subscribed to each button's change/blurRadio/keyup streams but only tore them down when the whole group was destroyed, so cycling buttons through a structural directive accumulated subscriptions for the lifetime of the group. The existing takeUntil(button.destroy$) never fired - destroy$ was declared on CheckboxBaseDirective but never completed - so the intended per-button cleanup was inert. Scope the subscriptions to the button's own DestroyRef instead, and cover it with a regression test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Document the OnPush switch for the checkbox, switch and radio components under Behavioral Changes, and the radio group's per-button subscription cleanup under Bug Fixes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was
linked to
issues
Sep 16, 2026
…into mdragnev/checkbox-signals-migration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related:
#17635 #17633 #17634
Description
Migrates
IgxCheckboxComponent,IgxSwitchComponent,IgxRadioComponentand their sharedCheckboxBaseDirectiveto Angular signals, and switches all three components fromChangeDetectionStrategy.EagertoOnPush.checked,disabled,readonly,indeterminate,invalid,focused,required,id,labelId,value,name,tabindex,labelPosition,disableRipple,ariaLabelledByandariaLabelnow read and writesignal()s behind their existing accessors.@Input()/@Output()decorator and plain property shape, socheckbox.checked = trueand[checked]="x"behave exactly as before. There is noinput()/output()/model()conversion.destroy$/takeUntilpair aroundngControl.statusChangesis replaced withtakeUntilDestroyed().destroy$was never completed anywhere, so it never actually fired.IgxRadioGroupDirectivenow releases its subscriptions to a radio button when that button is destroyed, rather than holding them until the whole group is destroyed.Deliberately left alone
requiredstays a getter rather than becoming acomputed(): it falls back tonativeElement.hasAttribute('required'), which is not part of the reactive graph, so memoizing it would go stale and silently drop the fallback.labelIdandariaLabelledBykeep their existing snapshot semantics — they captureidonce on initialization rather than deriving from it.Motivation / Context
Part of the ongoing move to signals and away from zone-based change detection.
Eager(CheckAlways) meant these components were re-checked on every change detection pass across the whole application. Backing the state with signals is what makesOnPushsafe here.The constraint was no public API changes and no breaking changes, which is why the
@Input()/@Output()decorators and accessor shapes are preserved rather than converted to signal inputs.Type of Change (check all that apply):
Component(s) / Area(s) Affected:
IgxCheckboxComponent,IgxSwitchComponent,IgxRadioComponent,IgxRadioGroupDirective,CheckboxBaseDirectiveHow Has This Been Tested?
Test Configuration:
Checklist:
feature/README.MDupdates for the feature docsREADME.MDCHANGELOG.MDupdates for newly added functionalityng updatemigrations for the breaking changes (migrations guidelines)