Skip to content

Commit 55cc197

Browse files
devversionjelbourn
authored andcommitted
fix: add event object for slide-toggle and checkbox. (#554)
* update(): add event object for slide-toggle and checkbox. Closes #552. * Fix Linters... again :P * update(): Address comments from @jelborun
1 parent e1ff202 commit 55cc197

File tree

5 files changed

+56
-35
lines changed

5 files changed

+56
-35
lines changed

src/components/checkbox/checkbox.spec.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {FORM_DIRECTIVES, NgModel, NgControl} from '@angular/common';
1111
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
1212
import {Component, DebugElement} from '@angular/core';
1313
import {By} from '@angular/platform-browser';
14-
import {MdCheckbox} from './checkbox';
14+
import {MdCheckbox, MdCheckboxChange} from './checkbox';
1515
import {PromiseCompleter} from '@angular2-material/core/async/promise-completer';
1616

1717

@@ -273,26 +273,24 @@ describe('MdCheckbox', () => {
273273
testComponent = fixture.debugElement.componentInstance;
274274
inputElement = <HTMLInputElement>checkboxNativeElement.querySelector('input');
275275
labelElement = <HTMLLabelElement>checkboxNativeElement.querySelector('label');
276-
277-
spyOn(testComponent, 'handleChange');
278276
});
279277
}));
280278

281279
it('should call the change event on first change after initialization', fakeAsync(() => {
282280
fixture.detectChanges();
283-
expect(testComponent.handleChange).not.toHaveBeenCalled();
281+
expect(testComponent.lastEvent).toBeUndefined();
284282

285283
checkboxInstance.checked = true;
286284
fixture.detectChanges();
287285

288286
tick();
289287

290-
expect(testComponent.handleChange).toHaveBeenCalledTimes(1);
291-
expect(testComponent.handleChange).toHaveBeenCalledWith(true);
288+
expect(testComponent.lastEvent.checked).toBe(true);
292289
}));
293290

294291
it('should not emit a DOM event to the change output', async(() => {
295-
expect(testComponent.handleChange).not.toHaveBeenCalled();
292+
fixture.detectChanges();
293+
expect(testComponent.lastEvent).toBeUndefined();
296294

297295
// Trigger the click on the inputElement, because the input will probably
298296
// emit a DOM event to the change output.
@@ -303,8 +301,7 @@ describe('MdCheckbox', () => {
303301
// We're checking the arguments type / emitted value to be a boolean, because sometimes the
304302
// emitted value can be a DOM Event, which is not valid.
305303
// See angular/angular#4059
306-
expect(testComponent.handleChange).toHaveBeenCalledTimes(1);
307-
expect(testComponent.handleChange).toHaveBeenCalledWith(true);
304+
expect(testComponent.lastEvent.checked).toBe(true);
308305
});
309306

310307
}));
@@ -541,8 +538,8 @@ class CheckboxWithNameAttribute {}
541538
/** Simple test component with change event */
542539
@Component({
543540
directives: [MdCheckbox],
544-
template: `<md-checkbox (change)="handleChange($event)"></md-checkbox>`
541+
template: `<md-checkbox (change)="lastEvent = $event"></md-checkbox>`
545542
})
546543
class CheckboxWithChangeEvent {
547-
handleChange(value: boolean): void {}
544+
lastEvent: MdCheckboxChange;
548545
}

src/components/checkbox/checkbox.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ enum TransitionCheckState {
4242
Indeterminate
4343
}
4444

45+
// A simple change event emitted by the MdCheckbox component.
46+
export class MdCheckboxChange {
47+
source: MdCheckbox;
48+
checked: boolean;
49+
}
50+
4551
/**
4652
* A material design checkbox component. Supports all of the functionality of an HTML5 checkbox,
4753
* and exposes a similar API. An MdCheckbox can be either checked, unchecked, indeterminate, or
@@ -105,7 +111,7 @@ export class MdCheckbox implements AfterContentInit, ControlValueAccessor {
105111
@Input() name: string = null;
106112

107113
/** Event emitted when the checkbox's `checked` value changes. */
108-
@Output() change: EventEmitter<boolean> = new EventEmitter<boolean>();
114+
@Output() change: EventEmitter<MdCheckboxChange> = new EventEmitter<MdCheckboxChange>();
109115

110116
/** Called when the checkbox is blurred. Needed to properly implement ControlValueAccessor. */
111117
onTouched: () => any = () => {};
@@ -144,7 +150,7 @@ export class MdCheckbox implements AfterContentInit, ControlValueAccessor {
144150

145151
// Only fire a change event if this isn't the first time the checked property is ever set.
146152
if (this._isInitialized) {
147-
this.change.emit(this._checked);
153+
this._emitChangeEvent();
148154
}
149155
}
150156
}
@@ -225,6 +231,14 @@ export class MdCheckbox implements AfterContentInit, ControlValueAccessor {
225231
}
226232
}
227233

234+
private _emitChangeEvent() {
235+
let event = new MdCheckboxChange();
236+
event.source = this;
237+
event.checked = this.checked;
238+
239+
this.change.emit(event);
240+
}
241+
228242
/**
229243
* Informs the component when the input has focus so that we can style accordingly
230244
* @internal
@@ -258,7 +272,7 @@ export class MdCheckbox implements AfterContentInit, ControlValueAccessor {
258272
onInteractionEvent(event: Event) {
259273
// We always have to stop propagation on the change event.
260274
// Otherwise the change event, from the input element, will bubble up and
261-
// emit its event object to the `change` output.
275+
// emit its event object to the `change` output.
262276
event.stopPropagation();
263277

264278
if (!this.disabled) {

src/components/radio/radio.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,20 +324,19 @@ describe('MdRadio', () => {
324324
radioInstances = radioDebugElements.map(debugEl => debugEl.componentInstance);
325325

326326
fixture.detectChanges();
327-
328-
spyOn(testComponent, 'onChange');
329327
});
330328
}));
331329

332330
it('should update the model before firing change event', fakeAsync(() => {
333331
expect(testComponent.modelValue).toBeUndefined();
332+
expect(testComponent.lastEvent).toBeUndefined();
334333

335334
groupInstance.value = 'chocolate';
336335
fixture.detectChanges();
337336

338337
tick();
339338
expect(testComponent.modelValue).toBe('chocolate');
340-
expect(testComponent.onChange).toHaveBeenCalledWith('chocolate');
339+
expect(testComponent.lastEvent.value).toBe('chocolate');
341340
}));
342341
});
343342

@@ -432,7 +431,7 @@ class StandaloneRadioButtons { }
432431
@Component({
433432
directives: [MD_RADIO_DIRECTIVES, FORM_DIRECTIVES],
434433
template: `
435-
<md-radio-group [(ngModel)]="modelValue" (change)="onChange(modelValue)">
434+
<md-radio-group [(ngModel)]="modelValue" (change)="lastEvent = $event">
436435
<md-radio-button *ngFor="let option of options" [value]="option.value">
437436
{{option.label}}
438437
</md-radio-button>
@@ -446,7 +445,7 @@ class RadioGroupWithNgModel {
446445
{label: 'Chocolate', value: 'chocolate'},
447446
{label: 'Strawberry', value: 'strawberry'},
448447
];
449-
onChange(value: MdRadioChange) {}
448+
lastEvent: MdRadioChange;
450449
}
451450

452451
// TODO(jelbourn): remove eveything below when Angular supports faking events.

src/components/slide-toggle/slide-toggle.spec.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ import {
44
expect,
55
beforeEach,
66
inject,
7-
async,
8-
fakeAsync,
7+
async
98
} from '@angular/core/testing';
109
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
1110
import {By} from '@angular/platform-browser';
1211
import {Component} from '@angular/core';
13-
import {MdSlideToggle} from './slide-toggle';
12+
import {MdSlideToggle, MdSlideToggleChange} from './slide-toggle';
1413
import {NgControl} from '@angular/common';
1514

1615
describe('MdSlideToggle', () => {
@@ -162,21 +161,21 @@ describe('MdSlideToggle', () => {
162161
expect(slideToggleElement.classList).not.toContain('ng-dirty');
163162
});
164163

165-
it('should emit the new values properly', fakeAsync(() => {
166-
spyOn(testComponent, 'onValueChange');
167-
164+
it('should emit the new values properly', async(() => {
168165
labelElement.click();
169166
fixture.detectChanges();
170167

171168
fixture.whenStable().then(() => {
172-
expect(testComponent.onValueChange).toHaveBeenCalledTimes(1);
173-
expect(testComponent.onValueChange).toHaveBeenCalledWith(true);
169+
// We're checking the arguments type / emitted value to be a boolean, because sometimes the
170+
// emitted value can be a DOM Event, which is not valid.
171+
// See angular/angular#4059
172+
expect(testComponent.lastEvent.checked).toBe(true);
174173
});
175174
}));
176175

177176
it('should support subscription on the change observable', () => {
178-
slideToggle.change.subscribe(value => {
179-
expect(value).toBe(true);
177+
slideToggle.change.subscribe((event: MdSlideToggleChange) => {
178+
expect(event.checked).toBe(true);
180179
});
181180

182181
slideToggle.toggle();
@@ -269,7 +268,7 @@ function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void
269268
<md-slide-toggle [(ngModel)]="slideModel" [disabled]="isDisabled" [color]="slideColor"
270269
[id]="slideId" [checked]="slideChecked" [name]="slideName"
271270
[aria-label]="slideLabel" [ariaLabel]="slideLabel"
272-
[ariaLabelledby]="slideLabelledBy" (change)="onValueChange($event)">
271+
[ariaLabelledby]="slideLabelledBy" (change)="lastEvent = $event">
273272
<span>Test Slide Toggle</span>
274273
</md-slide-toggle>
275274
`,
@@ -284,6 +283,5 @@ class SlideToggleTestApp {
284283
slideName: string;
285284
slideLabel: string;
286285
slideLabelledBy: string;
287-
288-
onValueChange(value: boolean): void {};
286+
lastEvent: MdSlideToggleChange;
289287
}

src/components/slide-toggle/slide-toggle.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {
2121
multi: true
2222
};
2323

24+
// A simple change event emitted by the MdSlideToggle component.
25+
export class MdSlideToggleChange {
26+
source: MdSlideToggle;
27+
checked: boolean;
28+
}
29+
2430
// Increasing integer for generating unique ids for slide-toggle components.
2531
let nextId = 0;
2632

@@ -59,8 +65,8 @@ export class MdSlideToggle implements ControlValueAccessor {
5965
@Input() ariaLabel: string = null;
6066
@Input() ariaLabelledby: string = null;
6167

62-
@Output('change') private _change: EventEmitter<boolean> = new EventEmitter<boolean>();
63-
change: Observable<boolean> = this._change.asObservable();
68+
private _change: EventEmitter<MdSlideToggleChange> = new EventEmitter<MdSlideToggleChange>();
69+
@Output() change: Observable<MdSlideToggleChange> = this._change.asObservable();
6470

6571
// Returns the unique id for the visual hidden input.
6672
getInputId = () => `${this.id || this._uniqueId}-input`;
@@ -144,7 +150,7 @@ export class MdSlideToggle implements ControlValueAccessor {
144150
if (this.checked !== !!value) {
145151
this._checked = value;
146152
this.onChange(this._checked);
147-
this._change.emit(this._checked);
153+
this._emitChangeEvent();
148154
}
149155
}
150156

@@ -173,6 +179,13 @@ export class MdSlideToggle implements ControlValueAccessor {
173179
}
174180
}
175181

182+
private _emitChangeEvent() {
183+
let event = new MdSlideToggleChange();
184+
event.source = this;
185+
event.checked = this.checked;
186+
this._change.emit(event);
187+
}
188+
176189
}
177190

178191
export const MD_SLIDE_TOGGLE_DIRECTIVES = [MdSlideToggle];

0 commit comments

Comments
 (0)