Skip to content

Commit 0340a41

Browse files
authored
fix(material/button-toggle): not preserving value if preselected option is removed (#27398)
The button toggle keeps track of the raw value to determine which option is preselected on init. The problem is that the raw value is never updated so if the user selects a different option, removes the preselected one and then re-adds it, the initial preselected option will become selected. Fixes #27397.
1 parent b985c7d commit 0340a41

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

src/material/button-toggle/button-toggle.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('MatButtonToggle with forms', () => {
1919
ButtonToggleGroupWithNgModel,
2020
ButtonToggleGroupWithFormControl,
2121
ButtonToggleGroupWithIndirectDescendantToggles,
22+
ButtonToggleGroupWithFormControlAndDynamicButtons,
2223
],
2324
});
2425

@@ -265,6 +266,45 @@ describe('MatButtonToggle with forms', () => {
265266
expect(fixture.componentInstance.control.value).toBe('red');
266267
expect(groupInstance._buttonToggles.length).toBe(3);
267268
}));
269+
270+
it('should preserve the selection if the pre-selected option is removed and re-added', () => {
271+
const fixture = TestBed.createComponent(ButtonToggleGroupWithFormControlAndDynamicButtons);
272+
const instance = fixture.componentInstance;
273+
instance.control.setValue('a');
274+
fixture.detectChanges();
275+
const buttons = fixture.nativeElement.querySelectorAll('.mat-button-toggle-button');
276+
277+
expect(instance.toggles.map(t => t.checked)).toEqual([true, false, false]);
278+
279+
buttons[2].click();
280+
fixture.detectChanges();
281+
282+
instance.values.shift();
283+
fixture.detectChanges();
284+
expect(instance.toggles.map(t => t.checked)).toEqual([false, true]);
285+
286+
instance.values.unshift('a');
287+
fixture.detectChanges();
288+
289+
expect(instance.toggles.map(t => t.checked)).toEqual([false, false, true]);
290+
});
291+
292+
it('should preserve the pre-selected option if it removed and re-added', () => {
293+
const fixture = TestBed.createComponent(ButtonToggleGroupWithFormControlAndDynamicButtons);
294+
const instance = fixture.componentInstance;
295+
instance.control.setValue('a');
296+
fixture.detectChanges();
297+
expect(instance.toggles.map(t => t.checked)).toEqual([true, false, false]);
298+
299+
instance.values.shift();
300+
fixture.detectChanges();
301+
expect(instance.toggles.map(t => t.checked)).toEqual([false, false]);
302+
303+
instance.values.unshift('a');
304+
fixture.detectChanges();
305+
306+
expect(instance.toggles.map(t => t.checked)).toEqual([true, false, false]);
307+
});
268308
});
269309

270310
describe('MatButtonToggle without forms', () => {
@@ -1097,3 +1137,16 @@ class ButtonToggleWithStaticChecked {
10971137
`,
10981138
})
10991139
class ButtonToggleWithStaticAriaAttributes {}
1140+
1141+
@Component({
1142+
template: `
1143+
<mat-button-toggle-group [formControl]="control">
1144+
<mat-button-toggle *ngFor="let value of values" [value]="value">{{value}}</mat-button-toggle>
1145+
</mat-button-toggle-group>
1146+
`,
1147+
})
1148+
class ButtonToggleGroupWithFormControlAndDynamicButtons {
1149+
@ViewChildren(MatButtonToggle) toggles: QueryList<MatButtonToggle>;
1150+
control = new FormControl('');
1151+
values = ['a', 'b', 'c'];
1152+
}

src/material/button-toggle/button-toggle.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
266266
/** Dispatch change event with current selection and group value. */
267267
_emitChangeEvent(toggle: MatButtonToggle): void {
268268
const event = new MatButtonToggleChange(toggle, this.value);
269+
this._rawValue = event.value;
269270
this._controlValueAccessorChangeFn(event.value);
270271
this.change.emit(event);
271272
}

0 commit comments

Comments
 (0)