diff --git a/src/components/checkbox/checkbox.spec.ts b/src/components/checkbox/checkbox.spec.ts
index 90ce7e8ee014..60c3c984b86d 100644
--- a/src/components/checkbox/checkbox.spec.ts
+++ b/src/components/checkbox/checkbox.spec.ts
@@ -286,7 +286,27 @@ describe('MdCheckbox', () => {
fixture.detectChanges();
tick();
- expect(testComponent.handleChange).toHaveBeenCalled();
+
+ expect(testComponent.handleChange).toHaveBeenCalledTimes(1);
+ expect(testComponent.handleChange).toHaveBeenCalledWith(true);
+ }));
+
+ it('should not emit a DOM event to the change output', async(() => {
+ expect(testComponent.handleChange).not.toHaveBeenCalled();
+
+ // Trigger the click on the inputElement, because the input will probably
+ // emit a DOM event to the change output.
+ inputElement.click();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ // We're checking the arguments type / emitted value to be a boolean, because sometimes the
+ // emitted value can be a DOM Event, which is not valid.
+ // See angular/angular#4059
+ expect(testComponent.handleChange).toHaveBeenCalledTimes(1);
+ expect(testComponent.handleChange).toHaveBeenCalledWith(true);
+ });
+
}));
});
@@ -521,8 +541,8 @@ class CheckboxWithNameAttribute {}
/** Simple test component with change event */
@Component({
directives: [MdCheckbox],
- template: ``
+ template: ``
})
class CheckboxWithChangeEvent {
- handleChange(): void {}
+ handleChange(value: boolean): void {}
}
diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts
index 4754b99ec617..4c1799bab7c6 100644
--- a/src/components/checkbox/checkbox.ts
+++ b/src/components/checkbox/checkbox.ts
@@ -242,14 +242,18 @@ export class MdCheckbox implements ControlValueAccessor {
}
/**
- * Event handler for checkbox input element. Toggles checked state if element is not disabled.
+ * Event handler for checkbox input element.
+ * Toggles checked state if element is not disabled.
* @param event
* @internal
*/
onInteractionEvent(event: Event) {
- if (this.disabled) {
- event.stopPropagation();
- } else {
+ // We always have to stop propagation on the change event.
+ // Otherwise the change event, from the input element, will bubble up and
+ // emit its event object to the `change` output.
+ event.stopPropagation();
+
+ if (!this.disabled) {
this.toggle();
}
}
diff --git a/src/components/radio/radio.html b/src/components/radio/radio.html
index 9edffea69521..e094b07f4842 100644
--- a/src/components/radio/radio.html
+++ b/src/components/radio/radio.html
@@ -13,7 +13,7 @@
[checked]="checked"
[disabled]="disabled"
[name]="name"
- (change)="onInputChange()"
+ (change)="onInputChange($event)"
(focus)="onInputFocus()"
(blur)="onInputBlur()" />
diff --git a/src/components/radio/radio.spec.ts b/src/components/radio/radio.spec.ts
index 2d07cc899da2..4515466de0fa 100644
--- a/src/components/radio/radio.spec.ts
+++ b/src/components/radio/radio.spec.ts
@@ -12,7 +12,7 @@ import {FORM_DIRECTIVES, NgControl} from '@angular/common';
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {Component, DebugElement, provide} from '@angular/core';
import {By} from '@angular/platform-browser';
-import {MD_RADIO_DIRECTIVES, MdRadioGroup, MdRadioButton} from './radio';
+import {MD_RADIO_DIRECTIVES, MdRadioGroup, MdRadioButton, MdRadioChange} from './radio';
import {MdRadioDispatcher} from './radio_dispatcher';
@@ -446,7 +446,7 @@ class RadioGroupWithNgModel {
{label: 'Chocolate', value: 'chocolate'},
{label: 'Strawberry', value: 'strawberry'},
];
- onChange(value: string) {}
+ onChange(value: MdRadioChange) {}
}
// TODO(jelbourn): remove eveything below when Angular supports faking events.
diff --git a/src/components/radio/radio.ts b/src/components/radio/radio.ts
index 7ca5fe887a2c..16c68ce7111c 100644
--- a/src/components/radio/radio.ts
+++ b/src/components/radio/radio.ts
@@ -391,7 +391,12 @@ export class MdRadioButton implements OnInit {
* Checks the radio due to an interaction with the underlying native
* @internal
*/
- onInputChange() {
+ onInputChange(event: Event) {
+ // We always have to stop propagation on the change event.
+ // Otherwise the change event, from the input element, will bubble up and
+ // emit its event object to the `change` output.
+ event.stopPropagation();
+
this.checked = true;
if (this.radioGroup) {
this.radioGroup.touch();
diff --git a/src/components/slide-toggle/slide-toggle.html b/src/components/slide-toggle/slide-toggle.html
index 6a3af979532f..595ea61e265a 100644
--- a/src/components/slide-toggle/slide-toggle.html
+++ b/src/components/slide-toggle/slide-toggle.html
@@ -17,7 +17,7 @@
[attr.aria-labelledby]="ariaLabelledby"
(blur)="onInputBlur()"
(focus)="onInputFocus()"
- (change)="onChangeEvent()">
+ (change)="onChangeEvent($event)">
diff --git a/src/components/slide-toggle/slide-toggle.spec.ts b/src/components/slide-toggle/slide-toggle.spec.ts
index 15cca9a781d5..eabae926fd81 100644
--- a/src/components/slide-toggle/slide-toggle.spec.ts
+++ b/src/components/slide-toggle/slide-toggle.spec.ts
@@ -5,6 +5,7 @@ import {
beforeEach,
inject,
async,
+ fakeAsync,
} from '@angular/core/testing';
import {TestComponentBuilder, ComponentFixture} from '@angular/compiler/testing';
import {By} from '@angular/platform-browser';
@@ -161,14 +162,17 @@ describe('MdSlideToggle', () => {
expect(slideToggleElement.classList).not.toContain('ng-dirty');
});
- it('should emit the new values', () => {
- expect(testComponent.changeCount).toBe(0);
+ it('should emit the new values properly', fakeAsync(() => {
+ spyOn(testComponent, 'onValueChange');
labelElement.click();
fixture.detectChanges();
- expect(testComponent.changeCount).toBe(1);
- });
+ fixture.whenStable().then(() => {
+ expect(testComponent.onValueChange).toHaveBeenCalledTimes(1);
+ expect(testComponent.onValueChange).toHaveBeenCalledWith(true);
+ });
+ }));
it('should support subscription on the change observable', () => {
slideToggle.change.subscribe(value => {
@@ -265,7 +269,7 @@ function dispatchFocusChangeEvent(eventName: string, element: HTMLElement): void
+ [ariaLabelledby]="slideLabelledBy" (change)="onValueChange($event)">
Test Slide Toggle
`,
@@ -280,5 +284,6 @@ class SlideToggleTestApp {
slideName: string;
slideLabel: string;
slideLabelledBy: string;
- changeCount: number = 0;
+
+ onValueChange(value: boolean): void {};
}
diff --git a/src/components/slide-toggle/slide-toggle.ts b/src/components/slide-toggle/slide-toggle.ts
index df1684231312..08b2cf1a71f0 100644
--- a/src/components/slide-toggle/slide-toggle.ts
+++ b/src/components/slide-toggle/slide-toggle.ts
@@ -75,7 +75,12 @@ export class MdSlideToggle implements ControlValueAccessor {
* which triggers a onChange event on click.
* @internal
*/
- onChangeEvent() {
+ onChangeEvent(event: Event) {
+ // We always have to stop propagation on the change event.
+ // Otherwise the change event, from the input element, will bubble up and
+ // emit its event object to the component's `change` output.
+ event.stopPropagation();
+
if (!this.disabled) {
this.toggle();
}