Skip to content

Commit 68d81a4

Browse files
authored
fix(material/radio): radio not preselected with static value and an ngIf (#25852)
Fixes that radio buttons weren't being preselected correctly when they have an `ngIf` and a static `value`. It appears that the issue was because we were subscribing to the `UniqueSelectionDispatcher` before the name of the radio button is assigned. Fixes #25831.
1 parent c8d8ef2 commit 68d81a4

File tree

5 files changed

+83
-8
lines changed

5 files changed

+83
-8
lines changed

src/material/legacy-radio/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ng_test_library(
5151
":legacy-radio",
5252
"//src/cdk/testing/private",
5353
"//src/material/radio",
54+
"@npm//@angular/common",
5455
"@npm//@angular/forms",
5556
"@npm//@angular/platform-browser",
5657
],

src/material/legacy-radio/radio.spec.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {waitForAsync, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
22
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
33
import {Component, DebugElement, ViewChild} from '@angular/core';
4+
import {CommonModule} from '@angular/common';
45
import {By} from '@angular/platform-browser';
56
import {dispatchFakeEvent} from '@angular/cdk/testing/private';
67

@@ -10,7 +11,7 @@ import {MatLegacyRadioButton, MatLegacyRadioGroup, MatLegacyRadioModule} from '.
1011
describe('MatRadio', () => {
1112
beforeEach(waitForAsync(() => {
1213
TestBed.configureTestingModule({
13-
imports: [MatLegacyRadioModule, FormsModule, ReactiveFormsModule],
14+
imports: [MatLegacyRadioModule, FormsModule, ReactiveFormsModule, CommonModule],
1415
declarations: [
1516
DisableableRadioButton,
1617
FocusableRadioButton,
@@ -23,6 +24,7 @@ describe('MatRadio', () => {
2324
RadioButtonWithPredefinedTabindex,
2425
RadioButtonWithPredefinedAriaAttributes,
2526
RadiosInsidePreCheckedRadioGroup,
27+
PreselectedRadioWithStaticValueAndNgIf,
2628
],
2729
});
2830

@@ -910,6 +912,14 @@ describe('MatRadio', () => {
910912
expect(groupInstance.selected).toBe(radioInstances[2]);
911913
});
912914
});
915+
916+
it('should preselect a radio button with a static value and an ngIf', () => {
917+
const fixture = TestBed.createComponent(PreselectedRadioWithStaticValueAndNgIf);
918+
fixture.detectChanges();
919+
920+
expect(fixture.componentInstance.preselectedGroup.value).toBe('b');
921+
expect(fixture.componentInstance.preselectedRadio.checked).toBe(true);
922+
});
913923
});
914924

915925
describe('MatRadioDefaultOverrides', () => {
@@ -1120,3 +1130,30 @@ class RadioButtonWithColorBinding {}
11201130
aria-labelledby="something-else"></mat-radio-button>`,
11211131
})
11221132
class RadioButtonWithPredefinedAriaAttributes {}
1133+
1134+
@Component({
1135+
// Note that this is somewhat of a contrived template, but it is required to
1136+
// reproduce the issue. It was taken for a specific user report at #25831.
1137+
template: `
1138+
<ng-container *ngIf="true">
1139+
<mat-radio-group [formControl]="controls.predecessor">
1140+
<mat-radio-button value="predecessor"></mat-radio-button>
1141+
</mat-radio-group>
1142+
</ng-container>
1143+
1144+
<mat-radio-group [formControl]="controls.target" #preselectedGroup>
1145+
<mat-radio-button value="a"></mat-radio-button>
1146+
<mat-radio-button *ngIf="true" value="b" #preselectedRadio></mat-radio-button>
1147+
</mat-radio-group>
1148+
`,
1149+
})
1150+
class PreselectedRadioWithStaticValueAndNgIf {
1151+
@ViewChild('preselectedGroup', {read: MatLegacyRadioGroup}) preselectedGroup: MatLegacyRadioGroup;
1152+
@ViewChild('preselectedRadio', {read: MatLegacyRadioButton})
1153+
preselectedRadio: MatLegacyRadioButton;
1154+
1155+
controls = {
1156+
predecessor: new FormControl('predecessor'),
1157+
target: new FormControl('b'),
1158+
};
1159+
}

src/material/radio/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ ng_test_library(
7070
deps = [
7171
":radio",
7272
"//src/cdk/testing/private",
73+
"@npm//@angular/common",
7374
"@npm//@angular/forms",
7475
"@npm//@angular/platform-browser",
7576
"@npm//@material/radio",

src/material/radio/radio.spec.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {waitForAsync, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
22
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
33
import {Component, DebugElement, ViewChild} from '@angular/core';
4+
import {CommonModule} from '@angular/common';
45
import {By} from '@angular/platform-browser';
56
import {dispatchFakeEvent} from '../../cdk/testing/private';
67
import {
@@ -14,7 +15,7 @@ import {
1415
describe('MDC-based MatRadio', () => {
1516
beforeEach(waitForAsync(() => {
1617
TestBed.configureTestingModule({
17-
imports: [MatRadioModule, FormsModule, ReactiveFormsModule],
18+
imports: [MatRadioModule, FormsModule, ReactiveFormsModule, CommonModule],
1819
declarations: [
1920
DisableableRadioButton,
2021
FocusableRadioButton,
@@ -27,6 +28,7 @@ describe('MDC-based MatRadio', () => {
2728
RadioButtonWithPredefinedTabindex,
2829
RadioButtonWithPredefinedAriaAttributes,
2930
RadiosInsidePreCheckedRadioGroup,
31+
PreselectedRadioWithStaticValueAndNgIf,
3032
],
3133
});
3234

@@ -927,6 +929,14 @@ describe('MDC-based MatRadio', () => {
927929
expect(groupInstance.selected).toBe(radioInstances[2]);
928930
});
929931
});
932+
933+
it('should preselect a radio button with a static value and an ngIf', () => {
934+
const fixture = TestBed.createComponent(PreselectedRadioWithStaticValueAndNgIf);
935+
fixture.detectChanges();
936+
937+
expect(fixture.componentInstance.preselectedGroup.value).toBe('b');
938+
expect(fixture.componentInstance.preselectedRadio.checked).toBe(true);
939+
});
930940
});
931941

932942
describe('MatRadioDefaultOverrides', () => {
@@ -1138,3 +1148,29 @@ class RadioButtonWithColorBinding {}
11381148
aria-labelledby="something-else"></mat-radio-button>`,
11391149
})
11401150
class RadioButtonWithPredefinedAriaAttributes {}
1151+
1152+
@Component({
1153+
// Note that this is somewhat of a contrived template, but it is required to
1154+
// reproduce the issue. It was taken for a specific user report at #25831.
1155+
template: `
1156+
<ng-container *ngIf="true">
1157+
<mat-radio-group [formControl]="controls.predecessor">
1158+
<mat-radio-button value="predecessor"></mat-radio-button>
1159+
</mat-radio-group>
1160+
</ng-container>
1161+
1162+
<mat-radio-group [formControl]="controls.target" #preselectedGroup>
1163+
<mat-radio-button value="a"></mat-radio-button>
1164+
<mat-radio-button *ngIf="true" value="b" #preselectedRadio></mat-radio-button>
1165+
</mat-radio-group>
1166+
`,
1167+
})
1168+
class PreselectedRadioWithStaticValueAndNgIf {
1169+
@ViewChild('preselectedGroup', {read: MatRadioGroup}) preselectedGroup: MatRadioGroup;
1170+
@ViewChild('preselectedRadio', {read: MatRadioButton}) preselectedRadio: MatRadioButton;
1171+
1172+
controls = {
1173+
predecessor: new FormControl('predecessor'),
1174+
target: new FormControl('b'),
1175+
};
1176+
}

src/material/radio/radio.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,6 @@ export abstract class _MatRadioButtonBase
510510
if (tabIndex) {
511511
this.tabIndex = coerceNumberProperty(tabIndex, 0);
512512
}
513-
514-
this._removeUniqueSelectionListener = _radioDispatcher.listen((id: string, name: string) => {
515-
if (id !== this.id && name === this.name) {
516-
this.checked = false;
517-
}
518-
});
519513
}
520514

521515
/** Focuses the radio button. */
@@ -550,6 +544,12 @@ export abstract class _MatRadioButtonBase
550544
// Copy name from parent radio group
551545
this.name = this.radioGroup.name;
552546
}
547+
548+
this._removeUniqueSelectionListener = this._radioDispatcher.listen((id, name) => {
549+
if (id !== this.id && name === this.name) {
550+
this.checked = false;
551+
}
552+
});
553553
}
554554

555555
ngDoCheck(): void {

0 commit comments

Comments
 (0)