Skip to content

Commit 7d360ac

Browse files
committed
fix(material/button-toggle): changed after checked error when updating tabindex (#31172)
Reworks the button toggle so it doesn't cause a "changed after checked" error when its tabindex is updated. (cherry picked from commit ad5ca9b)
1 parent 16493ec commit 7d360ac

File tree

2 files changed

+7
-52
lines changed

2 files changed

+7
-52
lines changed

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

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import {dispatchMouseEvent} from '@angular/cdk/testing/private';
2-
import {
3-
Component,
4-
DebugElement,
5-
provideCheckNoChangesConfig,
6-
QueryList,
7-
ViewChild,
8-
ViewChildren,
9-
} from '@angular/core';
2+
import {Component, DebugElement, QueryList, ViewChild, ViewChildren} from '@angular/core';
103
import {ComponentFixture, TestBed, fakeAsync, flush, tick} from '@angular/core/testing';
114
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
125
import {By} from '@angular/platform-browser';
@@ -19,21 +12,6 @@ import {
1912
} from './index';
2013

2114
describe('MatButtonToggle with forms', () => {
22-
beforeEach(fakeAsync(() => {
23-
TestBed.configureTestingModule({
24-
providers: [provideCheckNoChangesConfig({exhaustive: false})],
25-
imports: [
26-
MatButtonToggleModule,
27-
FormsModule,
28-
ReactiveFormsModule,
29-
ButtonToggleGroupWithNgModel,
30-
ButtonToggleGroupWithFormControl,
31-
ButtonToggleGroupWithIndirectDescendantToggles,
32-
ButtonToggleGroupWithFormControlAndDynamicButtons,
33-
],
34-
});
35-
}));
36-
3715
describe('using FormControl', () => {
3816
let fixture: ComponentFixture<ButtonToggleGroupWithFormControl>;
3917
let groupDebugElement: DebugElement;
@@ -333,27 +311,6 @@ describe('MatButtonToggle with forms', () => {
333311
});
334312

335313
describe('MatButtonToggle without forms', () => {
336-
beforeEach(fakeAsync(() => {
337-
TestBed.configureTestingModule({
338-
providers: [provideCheckNoChangesConfig({exhaustive: false})],
339-
imports: [
340-
MatButtonToggleModule,
341-
ButtonTogglesInsideButtonToggleGroup,
342-
ButtonTogglesInsideButtonToggleGroupMultiple,
343-
FalsyButtonTogglesInsideButtonToggleGroupMultiple,
344-
ButtonToggleGroupWithInitialValue,
345-
StandaloneButtonToggle,
346-
ButtonToggleWithAriaLabel,
347-
ButtonToggleWithAriaLabelledby,
348-
RepeatedButtonTogglesWithPreselectedValue,
349-
ButtonToggleWithTabindex,
350-
ButtonToggleWithStaticName,
351-
ButtonToggleWithStaticChecked,
352-
ButtonToggleWithStaticAriaAttributes,
353-
],
354-
});
355-
}));
356-
357314
describe('inside of an exclusive selection group', () => {
358315
let fixture: ComponentFixture<ButtonTogglesInsideButtonToggleGroup>;
359316
let groupDebugElement: DebugElement;

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ import {
3131
OnInit,
3232
Output,
3333
QueryList,
34+
signal,
3435
ViewChild,
3536
ViewEncapsulation,
37+
WritableSignal,
3638
} from '@angular/core';
3739
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
3840
import {_animationsDisabled, _StructuralStylesLoader, MatPseudoCheckbox, MatRipple} from '../core';
@@ -450,7 +452,6 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
450452
}
451453
}
452454
}
453-
this._markButtonsForCheck();
454455
}
455456

456457
/** Obtain the subsequent toggle to which the focus shifts. */
@@ -613,15 +614,12 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
613614
/** Tabindex of the toggle. */
614615
@Input()
615616
get tabIndex(): number | null {
616-
return this._tabIndex;
617+
return this._tabIndex();
617618
}
618619
set tabIndex(value: number | null) {
619-
if (value !== this._tabIndex) {
620-
this._tabIndex = value;
621-
this._markForCheck();
622-
}
620+
this._tabIndex.set(value);
623621
}
624-
private _tabIndex: number | null;
622+
private _tabIndex: WritableSignal<number | null>;
625623

626624
/** Whether ripples are disabled on the button toggle. */
627625
@Input({transform: booleanAttribute}) disableRipple: boolean;
@@ -691,7 +689,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
691689
{optional: true},
692690
);
693691

694-
this._tabIndex = parseInt(defaultTabIndex) || 0;
692+
this._tabIndex = signal<number | null>(parseInt(defaultTabIndex) || 0);
695693
this.buttonToggleGroup = toggleGroup;
696694
this.appearance =
697695
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';

0 commit comments

Comments
 (0)