Skip to content

Commit 5105a89

Browse files
committed
feat(select): emit change event
Adds an event to `md-select` that is emitted when the selected option has changed. Fixes #2248.
1 parent dccbe41 commit 5105a89

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

src/demo-app/select/select-demo.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="demo-select">
44
<div *ngIf="showSelect">
55
<md-card>
6-
<md-select placeholder="Food i would like to eat" [formControl]="foodControl">
6+
<md-select placeholder="Food i would like to eat" [formControl]="foodControl" (change)="changeListener($event)">
77
<md-option *ngFor="let food of foods" [value]="food.value"> {{ food.viewValue }} </md-option>
88
</md-select>
99
<p> Value: {{ foodControl.value }} </p>
@@ -19,7 +19,7 @@
1919

2020
<md-card>
2121
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="isRequired" [disabled]="isDisabled"
22-
#drinkControl="ngModel">
22+
(change)="changeListener($event)" #drinkControl="ngModel">
2323
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
2424
{{ drink.viewValue }}
2525
</md-option>

src/demo-app/select/select-demo.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {Component} from '@angular/core';
22
import {FormControl} from '@angular/forms';
3+
import {MdSelectChange} from '@angular/material';
34

45
@Component({
56
moduleId: module.id,
@@ -36,4 +37,7 @@ export class SelectDemo {
3637
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
3738
}
3839

40+
changeListener(event: MdSelectChange) {
41+
console.log(event);
42+
};
3943
}

src/lib/select/select.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,27 @@ describe('MdSelect', () => {
237237
expect(fixture.componentInstance.select.selected).not.toBeDefined();
238238
});
239239

240+
241+
it('should emit an event when the selected option has changed', () => {
242+
trigger.click();
243+
fixture.detectChanges();
244+
245+
(overlayContainerElement.querySelector('md-option') as HTMLElement).click();
246+
247+
expect(fixture.componentInstance.changeListener).toHaveBeenCalled();
248+
});
249+
250+
it('should not emit multiple change events for the same option', () => {
251+
trigger.click();
252+
fixture.detectChanges();
253+
254+
let option = overlayContainerElement.querySelector('md-option') as HTMLElement;
255+
256+
option.click();
257+
option.click();
258+
259+
expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
260+
});
240261
});
241262

242263
describe('forms integration', () => {
@@ -1146,7 +1167,8 @@ describe('MdSelect', () => {
11461167
selector: 'basic-select',
11471168
template: `
11481169
<div [style.height.px]="heightAbove"></div>
1149-
<md-select placeholder="Food" [formControl]="control" [required]="isRequired">
1170+
<md-select placeholder="Food" [formControl]="control" [required]="isRequired"
1171+
(change)="changeListener($event)">
11501172
<md-option *ngFor="let food of foods" [value]="food.value" [disabled]="food.disabled">
11511173
{{ food.viewValue }}
11521174
</md-option>
@@ -1169,6 +1191,7 @@ class BasicSelect {
11691191
isRequired: boolean;
11701192
heightAbove = 0;
11711193
heightBelow = 0;
1194+
changeListener = jasmine.createSpy('MdSelect change listener');
11721195

11731196
@ViewChild(MdSelect) select: MdSelect;
11741197
@ViewChildren(MdOption) options: QueryList<MdOption>;

src/lib/select/select.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ export const SELECT_PANEL_PADDING_Y = 16;
6464
*/
6565
export const SELECT_PANEL_VIEWPORT_PADDING = 8;
6666

67+
/** Change event object that is emitted when the select value has changed. */
68+
export class MdSelectChange {
69+
constructor(public source: MdSelect, public value: any) { }
70+
}
71+
6772
@Component({
6873
moduleId: module.id,
6974
selector: 'md-select, mat-select',
@@ -214,10 +219,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
214219
set required(value: any) { this._required = coerceBooleanProperty(value); }
215220

216221
/** Event emitted when the select has been opened. */
217-
@Output() onOpen = new EventEmitter();
222+
@Output() onOpen: EventEmitter<void> = new EventEmitter<void>();
218223

219224
/** Event emitted when the select has been closed. */
220-
@Output() onClose = new EventEmitter();
225+
@Output() onClose: EventEmitter<void> = new EventEmitter<void>();
226+
227+
/** Event emitted when the selected value has been changed by the user. */
228+
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
221229

222230
constructor(private _element: ElementRef, private _renderer: Renderer,
223231
private _viewportRuler: ViewportRuler, @Optional() private _dir: Dir,
@@ -429,8 +437,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
429437
private _listenToOptions(): void {
430438
this.options.forEach((option: MdOption) => {
431439
const sub = option.onSelect.subscribe((isUserInput: boolean) => {
432-
if (isUserInput) {
433-
this._onChange(option.value);
440+
if (isUserInput && this._selected !== option) {
441+
this._emitChangeEvent(option);
434442
}
435443
this._onSelect(option);
436444
});
@@ -444,6 +452,12 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
444452
this._subscriptions = [];
445453
}
446454

455+
/** Emits an event when the user selects an option. */
456+
private _emitChangeEvent(option: MdOption): void {
457+
this._onChange(option.value);
458+
this.change.emit(new MdSelectChange(this, option.value));
459+
}
460+
447461
/** Records option IDs to pass to the aria-owns property. */
448462
private _setOptionIds() {
449463
this._optionIds = this.options.map(option => option.id).join(' ');

0 commit comments

Comments
 (0)