Skip to content

Commit 9a3d3e8

Browse files
crisbetojosephperrott
authored andcommitted
fix(button-toggle): changed after checked error for repeated toggles with a preselected value (#10612)
1 parent 1d4f1d3 commit 9a3d3e8

File tree

2 files changed

+49
-11
lines changed

2 files changed

+49
-11
lines changed

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {fakeAsync, tick, ComponentFixture, TestBed} from '@angular/core/testing';
22
import {dispatchMouseEvent} from '@angular/cdk/testing';
33
import {NgModel, FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms';
4-
import {Component, DebugElement} from '@angular/core';
4+
import {Component, DebugElement, ViewChild, ViewChildren, QueryList} from '@angular/core';
55
import {By} from '@angular/platform-browser';
66
import {
77
MatButtonToggleGroup,
@@ -209,6 +209,7 @@ describe('MatButtonToggle without forms', () => {
209209
StandaloneButtonToggle,
210210
ButtonToggleWithAriaLabel,
211211
ButtonToggleWithAriaLabelledby,
212+
RepeatedButtonTogglesWithPreselectedValue,
212213
],
213214
});
214215

@@ -662,6 +663,14 @@ describe('MatButtonToggle without forms', () => {
662663
expect(inputElement.getAttribute('aria-labelledby')).toBe(null);
663664
});
664665
});
666+
667+
it('should not throw on init when toggles are repeated and there is an initial value', () => {
668+
const fixture = TestBed.createComponent(RepeatedButtonTogglesWithPreselectedValue);
669+
670+
expect(() => fixture.detectChanges()).not.toThrow();
671+
expect(fixture.componentInstance.toggleGroup.value).toBe('Two');
672+
expect(fixture.componentInstance.toggles.toArray()[1].checked).toBe(true);
673+
});
665674
});
666675

667676
@Component({
@@ -759,3 +768,21 @@ class ButtonToggleWithAriaLabel { }
759768
template: `<mat-button-toggle aria-labelledby="some-id"></mat-button-toggle>`
760769
})
761770
class ButtonToggleWithAriaLabelledby {}
771+
772+
773+
@Component({
774+
template: `
775+
<mat-button-toggle-group [(value)]="value">
776+
<mat-button-toggle *ngFor="let toggle of possibleValues" [value]="toggle">
777+
{{toggle}}
778+
</mat-button-toggle>
779+
</mat-button-toggle-group>
780+
`
781+
})
782+
class RepeatedButtonTogglesWithPreselectedValue {
783+
@ViewChild(MatButtonToggleGroup) toggleGroup: MatButtonToggleGroup;
784+
@ViewChildren(MatButtonToggle) toggles: QueryList<MatButtonToggle>;
785+
786+
possibleValues = ['One', 'Two', 'Three'];
787+
value = 'Two';
788+
}

src/lib/button-toggle/button-toggle.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,8 @@ export class MatButtonToggleGroup extends _MatButtonToggleGroupMixinBase impleme
180180
}
181181

182182
ngAfterContentInit() {
183-
// If there was an attempt to assign a value before init, use it to set the
184-
// initial selection, otherwise check the `checked` state of the toggles.
185-
if (typeof this._tempValue !== 'undefined') {
186-
this._setSelectionByValue(this._tempValue);
187-
this._tempValue = undefined;
188-
} else {
189-
this._selectionModel.select(...this._buttonToggles.filter(toggle => toggle.checked));
190-
}
183+
this._selectionModel.select(...this._buttonToggles.filter(toggle => toggle.checked));
184+
this._tempValue = undefined;
191185
}
192186

193187
/**
@@ -261,6 +255,19 @@ export class MatButtonToggleGroup extends _MatButtonToggleGroupMixinBase impleme
261255
return this._selectionModel.isSelected(toggle);
262256
}
263257

258+
/** Determines whether a button toggle should be checked on init. */
259+
_isPrechecked(toggle: MatButtonToggle) {
260+
if (typeof this._tempValue === 'undefined') {
261+
return false;
262+
}
263+
264+
if (this.multiple && Array.isArray(this._tempValue)) {
265+
return !!this._tempValue.find(value => toggle.value != null && value === toggle.value);
266+
}
267+
268+
return toggle.value === this._tempValue;
269+
}
270+
264271
/** Updates the selection state of the toggles in the group based on a value. */
265272
private _setSelectionByValue(value: any|any[]) {
266273
// If the toggles haven't been initialized yet, save the value for later.
@@ -409,6 +416,10 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
409416
this.name = this.buttonToggleGroup.name;
410417
}
411418

419+
if (this.buttonToggleGroup && this.buttonToggleGroup._isPrechecked(this)) {
420+
this.checked = true;
421+
}
422+
412423
this._focusMonitor.monitor(this._elementRef.nativeElement, true);
413424
}
414425

@@ -449,8 +460,8 @@ export class MatButtonToggle extends _MatButtonToggleMixinBase implements OnInit
449460
* update bound properties of the radio button.
450461
*/
451462
_markForCheck() {
452-
// When group value changes, the button will not be notified. Use `markForCheck` to explicit
453-
// update button toggle's status
463+
// When the group value changes, the button will not be notified.
464+
// Use `markForCheck` to explicit update button toggle's status.
454465
this._changeDetectorRef.markForCheck();
455466
}
456467
}

0 commit comments

Comments
 (0)