Skip to content

Commit 920c875

Browse files
authored
fix(radio): only call change callback with user input (#1521)
1 parent c9ef34c commit 920c875

File tree

2 files changed

+26
-22
lines changed

2 files changed

+26
-22
lines changed

src/lib/radio/radio.spec.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
1+
import {async, ComponentFixture, TestBed, fakeAsync, tick} from '@angular/core/testing';
22
import {NgControl, FormsModule} from '@angular/forms';
33
import {Component, DebugElement} from '@angular/core';
44
import {By} from '@angular/platform-browser';
@@ -176,12 +176,12 @@ describe('MdRadio', () => {
176176

177177
expect(nativeRadioInput.classList).not.toContain('md-radio-focused');
178178

179-
dispatchFocusChangeEvent('focus', nativeRadioInput);
179+
dispatchEvent('focus', nativeRadioInput);
180180
fixture.detectChanges();
181181

182182
expect(radioNativeElements[0].classList).toContain('md-radio-focused');
183183

184-
dispatchFocusChangeEvent('blur', nativeRadioInput);
184+
dispatchEvent('blur', nativeRadioInput);
185185
fixture.detectChanges();
186186

187187
expect(radioNativeElements[0].classList).not.toContain('md-radio-focused');
@@ -223,7 +223,7 @@ describe('MdRadio', () => {
223223
let groupDebugElement: DebugElement;
224224
let groupNativeElement: HTMLElement;
225225
let radioDebugElements: DebugElement[];
226-
let radioNativeElements: HTMLElement[];
226+
let innerRadios: DebugElement[];
227227
let radioLabelElements: HTMLLabelElement[];
228228
let groupInstance: MdRadioGroup;
229229
let radioInstances: MdRadioButton[];
@@ -242,8 +242,8 @@ describe('MdRadio', () => {
242242
groupNgControl = groupDebugElement.injector.get(NgControl);
243243

244244
radioDebugElements = fixture.debugElement.queryAll(By.directive(MdRadioButton));
245-
radioNativeElements = radioDebugElements.map(debugEl => debugEl.nativeElement);
246245
radioInstances = radioDebugElements.map(debugEl => debugEl.componentInstance);
246+
innerRadios = fixture.debugElement.queryAll(By.css('input[type="radio"]'));
247247

248248
radioLabelElements = radioDebugElements
249249
.map(debugEl => debugEl.query(By.css('label')).nativeElement);
@@ -280,16 +280,16 @@ describe('MdRadio', () => {
280280
expect(groupNgControl.pristine).toBe(true);
281281
expect(groupNgControl.touched).toBe(false);
282282

283-
// After changing the value programmatically, the control should become dirty (not pristine),
283+
// After changing the value programmatically, the control should stay pristine
284284
// but remain untouched.
285285
radioInstances[1].checked = true;
286286
fixture.detectChanges();
287287

288288
expect(groupNgControl.valid).toBe(true);
289-
expect(groupNgControl.pristine).toBe(false);
289+
expect(groupNgControl.pristine).toBe(true);
290290
expect(groupNgControl.touched).toBe(false);
291291

292-
// After a user interaction occurs (such as a click), the control should remain dirty and
292+
// After a user interaction occurs (such as a click), the control should become dirty and
293293
// now also be touched.
294294
radioLabelElements[2].click();
295295
fixture.detectChanges();
@@ -299,27 +299,31 @@ describe('MdRadio', () => {
299299
expect(groupNgControl.touched).toBe(true);
300300
});
301301

302-
it('should update the ngModel value when selecting a radio button', () => {
303-
radioInstances[1].checked = true;
302+
it('should write to the radio button based on ngModel', fakeAsync(() => {
303+
testComponent.modelValue = 'chocolate';
304+
fixture.detectChanges();
305+
tick();
304306
fixture.detectChanges();
305307

308+
expect(innerRadios[1].nativeElement.checked).toBe(true);
309+
}));
310+
311+
it('should update the ngModel value when selecting a radio button', () => {
312+
dispatchEvent('change', innerRadios[1].nativeElement);
313+
fixture.detectChanges();
306314
expect(testComponent.modelValue).toBe('chocolate');
307315
});
308316

309317
it('should update the model before firing change event', () => {
310318
expect(testComponent.modelValue).toBeUndefined();
311319
expect(testComponent.lastEvent).toBeUndefined();
312320

313-
groupInstance.value = 'chocolate';
321+
dispatchEvent('change', innerRadios[1].nativeElement);
314322
fixture.detectChanges();
315-
316-
expect(testComponent.modelValue).toBe('chocolate');
317323
expect(testComponent.lastEvent.value).toBe('chocolate');
318324

319-
groupInstance.value = 'vanilla';
325+
dispatchEvent('change', innerRadios[0].nativeElement);
320326
fixture.detectChanges();
321-
322-
expect(testComponent.modelValue).toBe('vanilla');
323327
expect(testComponent.lastEvent.value).toBe('vanilla');
324328
});
325329
});
@@ -484,14 +488,14 @@ class RadioGroupWithNgModel {
484488
lastEvent: MdRadioChange;
485489
}
486490

487-
// TODO(jelbourn): remove eveything below when Angular supports faking events.
491+
// TODO(jelbourn): remove everything below when Angular supports faking events.
488492

489493
/**
490-
* Dispatches a focus change event from an element.
491-
* @param eventName Name of the event, either 'focus' or 'blur'.
494+
* Dispatches an event from an element.
495+
* @param eventName Name of the event
492496
* @param element The element from which the event will be dispatched.
493497
*/
494-
function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void {
498+
function dispatchEvent(eventName: string, element: HTMLElement): void {
495499
let event = document.createEvent('Event');
496500
event.initEvent(eventName, true, true);
497501
element.dispatchEvent(event);

src/lib/radio/radio.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
7878
private _isInitialized: boolean = false;
7979

8080
/** The method to be called in order to update ngModel */
81-
private _controlValueAccessorChangeFn: (value: any) => void = (value) => {};
81+
_controlValueAccessorChangeFn: (value: any) => void = (value) => {};
8282

8383
/** onTouch function registered via registerOnTouch (ControlValueAccessor). */
8484
onTouched: () => any = () => {};
@@ -198,7 +198,6 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
198198
let event = new MdRadioChange();
199199
event.source = this._selected;
200200
event.value = this._value;
201-
this._controlValueAccessorChangeFn(event.value);
202201
this.change.emit(event);
203202
}
204203

@@ -405,6 +404,7 @@ export class MdRadioButton implements OnInit {
405404
event.stopPropagation();
406405

407406
this.checked = true;
407+
this.radioGroup._controlValueAccessorChangeFn(this.value);
408408
this._emitChangeEvent();
409409

410410
if (this.radioGroup) {

0 commit comments

Comments
 (0)