Skip to content

Commit f8c5be8

Browse files
tinayuangaoandrewseguin
authored andcommitted
fix(button-toggle): remove emit change event when value changes (#6034)
1 parent 05ca4a7 commit f8c5be8

File tree

2 files changed

+28
-69
lines changed

2 files changed

+28
-69
lines changed

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

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ describe('MdButtonToggle with forms', () => {
8787
let buttonToggleInstances: MdButtonToggle[];
8888
let testComponent: ButtonToggleGroupWithNgModel;
8989
let groupNgModel: NgModel;
90+
let buttonToggleLabels: HTMLElement[];
9091

9192
beforeEach(async(() => {
9293
fixture = TestBed.createComponent(ButtonToggleGroupWithNgModel);
93-
94+
fixture.detectChanges();
9495
testComponent = fixture.debugElement.componentInstance;
9596

9697
groupDebugElement = fixture.debugElement.query(By.directive(MdButtonToggleGroup));
@@ -102,6 +103,8 @@ describe('MdButtonToggle with forms', () => {
102103
buttonToggleNativeElements =
103104
buttonToggleDebugElements.map(debugEl => debugEl.nativeElement);
104105
buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
106+
buttonToggleLabels = buttonToggleDebugElements.map(
107+
debugEl => debugEl.query(By.css('label')).nativeElement);
105108

106109
fixture.detectChanges();
107110
}));
@@ -110,42 +113,13 @@ describe('MdButtonToggle with forms', () => {
110113
expect(testComponent.modelValue).toBeUndefined();
111114
expect(testComponent.lastEvent).toBeUndefined();
112115

113-
groupInstance.value = 'red';
116+
buttonToggleLabels[0].click();
114117
fixture.detectChanges();
115118

116119
tick();
117120
expect(testComponent.modelValue).toBe('red');
118121
expect(testComponent.lastEvent.value).toBe('red');
119122
}));
120-
});
121-
122-
describe('button toggle group with ngModel', () => {
123-
let fixture: ComponentFixture<ButtonToggleGroupWithNgModel>;
124-
let groupDebugElement: DebugElement;
125-
let groupNativeElement: HTMLElement;
126-
let buttonToggleDebugElements: DebugElement[];
127-
let buttonToggleNativeElements: HTMLElement[];
128-
let groupInstance: MdButtonToggleGroup;
129-
let buttonToggleInstances: MdButtonToggle[];
130-
let testComponent: ButtonToggleGroupWithNgModel;
131-
let groupNgModel: NgModel;
132-
133-
beforeEach(async(() => {
134-
fixture = TestBed.createComponent(ButtonToggleGroupWithNgModel);
135-
fixture.detectChanges();
136-
137-
testComponent = fixture.debugElement.componentInstance;
138-
139-
groupDebugElement = fixture.debugElement.query(By.directive(MdButtonToggleGroup));
140-
groupNativeElement = groupDebugElement.nativeElement;
141-
groupInstance = groupDebugElement.injector.get<MdButtonToggleGroup>(MdButtonToggleGroup);
142-
groupNgModel = groupDebugElement.injector.get<NgModel>(NgModel);
143-
144-
buttonToggleDebugElements = fixture.debugElement.queryAll(By.directive(MdButtonToggle));
145-
buttonToggleNativeElements =
146-
buttonToggleDebugElements.map(debugEl => debugEl.nativeElement);
147-
buttonToggleInstances = buttonToggleDebugElements.map(debugEl => debugEl.componentInstance);
148-
}));
149123

150124
it('should set individual radio names based on the group name', () => {
151125
expect(groupInstance.name).toBeTruthy();
@@ -183,11 +157,10 @@ describe('MdButtonToggle with forms', () => {
183157
tick();
184158

185159
expect(groupNgModel.valid).toBe(true);
186-
expect(groupNgModel.pristine).toBe(false);
160+
expect(groupNgModel.pristine).toBe(true);
187161
expect(groupNgModel.touched).toBe(false);
188162

189-
let nativeRadioLabel = buttonToggleDebugElements[2].query(By.css('label')).nativeElement;
190-
nativeRadioLabel.click();
163+
buttonToggleLabels[2].click();
191164
fixture.detectChanges();
192165
tick();
193166

@@ -197,7 +170,7 @@ describe('MdButtonToggle with forms', () => {
197170
}));
198171

199172
it('should update the ngModel value when selecting a button toggle', fakeAsync(() => {
200-
buttonToggleInstances[1].checked = true;
173+
buttonToggleLabels[1].click();
201174
fixture.detectChanges();
202175

203176
tick();
@@ -276,9 +249,7 @@ describe('MdButtonToggle without forms', () => {
276249

277250
it('should update the group value when one of the toggles changes', () => {
278251
expect(groupInstance.value).toBeFalsy();
279-
let nativeCheckboxLabel = buttonToggleDebugElements[0].query(By.css('label')).nativeElement;
280-
281-
nativeCheckboxLabel.click();
252+
buttonToggleLabelElements[0].click();
282253
fixture.detectChanges();
283254

284255
expect(groupInstance.value).toBe('test1');
@@ -287,19 +258,15 @@ describe('MdButtonToggle without forms', () => {
287258

288259
it('should update the group and toggles when one of the button toggles is clicked', () => {
289260
expect(groupInstance.value).toBeFalsy();
290-
let nativeCheckboxLabel = buttonToggleDebugElements[0].query(By.css('label')).nativeElement;
291-
292-
nativeCheckboxLabel.click();
261+
buttonToggleLabelElements[0].click();
293262
fixture.detectChanges();
294263

295264
expect(groupInstance.value).toBe('test1');
296265
expect(groupInstance.selected).toBe(buttonToggleInstances[0]);
297266
expect(buttonToggleInstances[0].checked).toBe(true);
298267
expect(buttonToggleInstances[1].checked).toBe(false);
299268

300-
let nativeCheckboxLabel2 = buttonToggleDebugElements[1].query(By.css('label')).nativeElement;
301-
302-
nativeCheckboxLabel2.click();
269+
buttonToggleLabelElements[1].click();
303270
fixture.detectChanges();
304271

305272
expect(groupInstance.value).toBe('test2');
@@ -309,9 +276,7 @@ describe('MdButtonToggle without forms', () => {
309276
});
310277

311278
it('should check a button toggle upon interaction with underlying native radio button', () => {
312-
let nativeRadioInput = buttonToggleDebugElements[0].query(By.css('input')).nativeElement;
313-
314-
nativeRadioInput.click();
279+
buttonToggleLabelElements[0].click();
315280
fixture.detectChanges();
316281

317282
expect(buttonToggleInstances[0].checked).toBe(true);
@@ -354,12 +319,12 @@ describe('MdButtonToggle without forms', () => {
354319
let changeSpy = jasmine.createSpy('button-toggle-group change listener');
355320
groupInstance.change.subscribe(changeSpy);
356321

357-
groupInstance.value = 'test1';
322+
buttonToggleLabelElements[0].click();
358323
fixture.detectChanges();
359324
tick();
360325
expect(changeSpy).toHaveBeenCalled();
361326

362-
groupInstance.value = 'test2';
327+
buttonToggleLabelElements[1].click();
363328
fixture.detectChanges();
364329
tick();
365330
expect(changeSpy).toHaveBeenCalledTimes(2);
@@ -414,7 +379,7 @@ describe('MdButtonToggle without forms', () => {
414379
fixture.detectChanges();
415380

416381
expect(groupInstance.value).toBe('green');
417-
expect(testComponent.lastEvent.value).toBe('green');
382+
expect(testComponent.lastEvent).toBeFalsy();
418383
});
419384

420385
});

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

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
ViewChild,
2424
ViewEncapsulation,
2525
forwardRef,
26-
AfterViewInit,
2726
ChangeDetectionStrategy,
2827
ChangeDetectorRef,
2928
} from '@angular/core';
@@ -72,8 +71,8 @@ export class MdButtonToggleChange {
7271
},
7372
exportAs: 'mdButtonToggleGroup',
7473
})
75-
export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implements AfterViewInit,
76-
ControlValueAccessor, CanDisable {
74+
export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase
75+
implements ControlValueAccessor, CanDisable {
7776

7877
/** The value for the button toggle group. Should match currently selected button toggle. */
7978
private _value: any = null;
@@ -87,25 +86,18 @@ export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implement
8786
/** The currently selected button toggle, should match the value. */
8887
private _selected: MdButtonToggle | null = null;
8988

90-
/** Whether the button toggle group is initialized or not. */
91-
private _isInitialized: boolean = false;
92-
9389
/**
9490
* The method to be called in order to update ngModel.
9591
* Now `ngModel` binding is not supported in multiple selection mode.
9692
*/
97-
private _controlValueAccessorChangeFn: (value: any) => void = () => {};
93+
_controlValueAccessorChangeFn: (value: any) => void = () => {};
9894

9995
/** onTouch function registered via registerOnTouch (ControlValueAccessor). */
10096
onTouched: () => any = () => {};
10197

10298
/** Child button toggle buttons. */
10399
@ContentChildren(forwardRef(() => MdButtonToggle)) _buttonToggles: QueryList<MdButtonToggle>;
104100

105-
ngAfterViewInit() {
106-
this._isInitialized = true;
107-
}
108-
109101
/** `name` attribute for the underlying `input` element. */
110102
@Input()
111103
get name(): string {
@@ -132,18 +124,11 @@ export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implement
132124
get value(): any {
133125
return this._value;
134126
}
135-
136127
set value(newValue: any) {
137128
if (this._value != newValue) {
138129
this._value = newValue;
139130

140131
this._updateSelectedButtonToggleFromValue();
141-
142-
// Only emit a change event if the view is completely initialized.
143-
// We don't want to emit a change event for the initial values.
144-
if (this._isInitialized) {
145-
this._emitChangeEvent();
146-
}
147132
}
148133
}
149134

@@ -165,6 +150,10 @@ export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implement
165150
/** Event emitted when the group's value changes. */
166151
@Output() change: EventEmitter<MdButtonToggleChange> = new EventEmitter<MdButtonToggleChange>();
167152

153+
constructor(private _changeDetector: ChangeDetectorRef) {
154+
super();
155+
}
156+
168157
private _updateButtonToggleNames(): void {
169158
if (this._buttonToggles) {
170159
this._buttonToggles.forEach((toggle) => {
@@ -193,7 +182,7 @@ export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implement
193182
}
194183

195184
/** Dispatch change event with current selection and group value. */
196-
private _emitChangeEvent(): void {
185+
_emitChangeEvent(): void {
197186
let event = new MdButtonToggleChange();
198187
event.source = this._selected;
199188
event.value = this._value;
@@ -207,6 +196,7 @@ export class MdButtonToggleGroup extends _MdButtonToggleGroupMixinBase implement
207196
*/
208197
writeValue(value: any) {
209198
this.value = value;
199+
this._changeDetector.markForCheck();
210200
}
211201

212202
/**
@@ -438,9 +428,13 @@ export class MdButtonToggle implements OnInit, OnDestroy {
438428
if (this._isSingleSelector) {
439429
// Propagate the change one-way via the group, which will in turn mark this
440430
// button toggle as checked.
431+
let groupValueChanged = this.buttonToggleGroup.selected != this;
441432
this.checked = true;
442433
this.buttonToggleGroup.selected = this;
443434
this.buttonToggleGroup.onTouched();
435+
if (groupValueChanged) {
436+
this.buttonToggleGroup._emitChangeEvent();
437+
}
444438
} else {
445439
this._toggle();
446440
}

0 commit comments

Comments
 (0)