Skip to content

Commit 8af16e8

Browse files
fix(radio): unsetting the model will deselect all radio buttons (#441)
closes #327
1 parent ada285c commit 8af16e8

File tree

2 files changed

+77
-44
lines changed

2 files changed

+77
-44
lines changed

Diff for: src/components/radio/radio.spec.ts

+62-36
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export function main() {
2222
builder = tcb;
2323
}));
2424

25-
it('should have same name as radio group', (done: () => void) => {
26-
builder
25+
it('should have same name as radio group', () => {
26+
return builder
2727
.overrideTemplate(TestApp, `
2828
<md-radio-group name="my_group">
2929
<md-radio-button></md-radio-button>
@@ -34,11 +34,11 @@ export function main() {
3434

3535
fixture.detectChanges();
3636
expect(button.componentInstance.name).toBe('my_group');
37-
}).then(done);
37+
});
3838
});
3939

40-
it('should not allow click selection if disabled', (done: () => void) => {
41-
builder
40+
it('should not allow click selection if disabled', () => {
41+
return builder
4242
.overrideTemplate(TestApp, '<md-radio-button disabled></md-radio-button>')
4343
.createAsync(TestApp)
4444
.then(fixture => {
@@ -49,11 +49,11 @@ export function main() {
4949

5050
button.nativeElement.click();
5151
expect(button.componentInstance.checked).toBe(false);
52-
}).then(done);
52+
});
5353
});
5454

55-
it('should be disabled if radio group disabled', (done: () => void) => {
56-
builder
55+
it('should be disabled if radio group disabled', () => {
56+
return builder
5757
.overrideTemplate(TestApp, `
5858
<md-radio-group disabled>
5959
<md-radio-button></md-radio-button>
@@ -64,11 +64,11 @@ export function main() {
6464

6565
fixture.detectChanges();
6666
expect(button.componentInstance.disabled).toBe(true);
67-
}).then(done);
67+
});
6868
});
6969

70-
it('updates parent group value when selected and value changed', (done: () => void) => {
71-
builder
70+
it('updates parent group value when selected and value changed', () => {
71+
return builder
7272
.overrideTemplate(TestApp, `
7373
<md-radio-group>
7474
<md-radio-button value="1"></md-radio-button>
@@ -86,11 +86,11 @@ export function main() {
8686
button.componentInstance.value = '2';
8787
fixture.detectChanges();
8888
expect(radioGroupInstance.value).toBe('2');
89-
}).then(done);
89+
});
9090
});
9191

92-
it('should be checked after input change event', (done: () => void) => {
93-
builder
92+
it('should be checked after input change event', () => {
93+
return builder
9494
.overrideTemplate(TestApp, '<md-radio-button></md-radio-button>')
9595
.createAsync(TestApp)
9696
.then(fixture => {
@@ -103,11 +103,11 @@ export function main() {
103103
let event = createEvent('change');
104104
input.nativeElement.dispatchEvent(event);
105105
expect(button.componentInstance.checked).toBe(true);
106-
}).then(done);
106+
});
107107
});
108108

109-
it('should emit event when checked', (done: () => void) => {
110-
builder
109+
it('should emit event when checked', () => {
110+
return builder
111111
.overrideTemplate(TestApp, '<md-radio-button></md-radio-button>')
112112
.createAsync(TestApp)
113113
.then(fixture => {
@@ -124,11 +124,11 @@ export function main() {
124124
expect(changeEvent).not.toBe(null);
125125
expect(changeEvent.source).toBe(button.componentInstance);
126126
});
127-
}).then(done);
127+
});
128128
});
129129

130-
it('should be focusable', (done: () => void) => {
131-
builder
130+
it('should be focusable', () => {
131+
return builder
132132
.overrideTemplate(TestApp, '<md-radio-button></md-radio-button>')
133133
.createAsync(TestApp)
134134
.then(fixture => {
@@ -147,7 +147,7 @@ export function main() {
147147
input.nativeElement.dispatchEvent(event);
148148
fixture.detectChanges();
149149
expect(button.nativeElement.classList.contains('md-radio-focused')).toBe(false);
150-
}).then(done);
150+
});
151151
});
152152
});
153153

@@ -183,8 +183,8 @@ export function main() {
183183
builder = tcb;
184184
}));
185185

186-
it('can select by value', (done: () => void) => {
187-
builder
186+
it('can select by value', () => {
187+
return builder
188188
.overrideTemplate(TestApp, `
189189
<md-radio-group>
190190
<md-radio-button value="1"></md-radio-button>
@@ -203,11 +203,11 @@ export function main() {
203203

204204
fixture.detectChanges();
205205
expect(radioGroupInstance.selected).toBe(buttons[1].componentInstance);
206-
}).then(done);
206+
});
207207
});
208208

209-
it('should select uniquely', (done: () => void) => {
210-
builder
209+
it('should select uniquely', () => {
210+
return builder
211211
.overrideTemplate(TestApp, `
212212
<md-radio-group>
213213
<md-radio-button></md-radio-button>
@@ -229,11 +229,11 @@ export function main() {
229229
radioGroupInstance.selected = buttons[1].componentInstance;
230230
fixture.detectChanges();
231231
expect(isSinglySelected(buttons[1], buttons)).toBe(true);
232-
}).then(done);
232+
});
233233
});
234234

235-
it('should emit event when value changes', (done: () => void) => {
236-
builder
235+
it('should emit event when value changes', () => {
236+
return builder
237237
.overrideTemplate(TestApp, `
238238
<md-radio-group>
239239
<md-radio-button></md-radio-button>
@@ -258,11 +258,11 @@ export function main() {
258258
expect(changeEvent).not.toBe(null);
259259
expect(changeEvent.source).toBe(buttons[1].componentInstance);
260260
});
261-
}).then(done);
261+
});
262262
});
263263

264-
it('should bind value to model without initial value', (done: () => void) => {
265-
builder
264+
it('should bind value to model without initial value', () => {
265+
return builder
266266
.overrideTemplate(TestApp, `
267267
<md-radio-group [(ngModel)]="choice">
268268
<md-radio-button [value]="0"></md-radio-button>
@@ -290,11 +290,11 @@ export function main() {
290290
expect(isSinglySelected(buttons[1], buttons)).toBe(true);
291291
expect(fixture.componentInstance.choice).toBe(1);
292292
});
293-
}).then(done);
293+
});
294294
});
295295

296-
it('should bind value to model with initial value', (done: () => void) => {
297-
builder
296+
it('should bind value to model with initial value', () => {
297+
return builder
298298
.overrideTemplate(TestAppWithInitialValue, `
299299
<md-radio-group [(ngModel)]="choice">
300300
<md-radio-button [value]="0"></md-radio-button>
@@ -321,7 +321,29 @@ export function main() {
321321
expect(isSinglySelected(buttons[1], buttons)).toBe(true);
322322
expect(fixture.componentInstance.choice).toBe(1);
323323
});
324-
}).then(done);
324+
});
325+
});
326+
327+
it('should deselect all buttons when model is null or undefined', () => {
328+
return builder
329+
.overrideTemplate(TestAppWithInitialValue, `
330+
<md-radio-group [ngModel]="choice">
331+
<md-radio-button [value]="0"></md-radio-button>
332+
<md-radio-button [value]="1"></md-radio-button>
333+
</md-radio-group>
334+
`)
335+
.createAsync(TestAppWithInitialValue)
336+
.then(fixture => {
337+
let buttons = fixture.debugElement.queryAll(By.css('md-radio-button'));
338+
339+
fixture.componentInstance.choice = 0;
340+
fixture.detectChanges();
341+
expect(isSinglySelected(buttons[0], buttons)).toBe(true);
342+
343+
fixture.componentInstance.choice = null;
344+
fixture.detectChanges();
345+
expect(allDeselected(buttons)).toBe(true);
346+
});
325347
});
326348

327349
});
@@ -336,6 +358,10 @@ function isSinglySelected(button: DebugElement, buttons: DebugElement[]): boolea
336358
return component.checked && otherSelectedButtons.length == 0;
337359
}
338360

361+
function allDeselected(buttons: DebugElement[]): boolean {
362+
return buttons.every(debugEl => !debugEl.componentInstance.checked);
363+
}
364+
339365
/** Browser-agnostic function for creating an event. */
340366
function createEvent(name: string): Event {
341367
let ev: Event;

Diff for: src/components/radio/radio.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,14 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
150150
});
151151

152152
if (matched.length == 0) {
153-
// Didn't find a button that matches this value, return early without setting.
154-
return;
153+
// When the value of the group is cleared to null, deselect all radio button in the group.
154+
if (this.value == null) {
155+
this.selected = null;
156+
this._radios.forEach(radio => radio.checked = false);
157+
}
158+
} else {
159+
this.selected = matched[0];
155160
}
156-
157-
// Change the selection immediately.
158-
this.selected = matched[0];
159161
}
160162
}
161163

@@ -173,10 +175,15 @@ export class MdRadioGroup implements AfterContentInit, ControlValueAccessor {
173175
}
174176

175177
set selected(selected: MdRadioButton) {
176-
this._selected = selected;
177-
this.value = selected.value;
178+
if (selected) {
179+
this._selected = selected;
180+
this.value = selected.value;
178181

179-
selected.checked = true;
182+
selected.checked = true;
183+
} else {
184+
this._selected = null;
185+
this._value = null;
186+
}
180187
}
181188

182189
/** Implemented as part of ControlValueAccessor. */

0 commit comments

Comments
 (0)