Skip to content

Commit ec89251

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 ec89251

File tree

4 files changed

+53
-7
lines changed

4 files changed

+53
-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: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ 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+
source: MdSelect;
70+
value: any;
71+
}
72+
6773
@Component({
6874
moduleId: module.id,
6975
selector: 'md-select, mat-select',
@@ -214,10 +220,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
214220
set required(value: any) { this._required = coerceBooleanProperty(value); }
215221

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

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

222231
constructor(private _element: ElementRef, private _renderer: Renderer,
223232
private _viewportRuler: ViewportRuler, @Optional() private _dir: Dir,
@@ -429,8 +438,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
429438
private _listenToOptions(): void {
430439
this.options.forEach((option: MdOption) => {
431440
const sub = option.onSelect.subscribe((isUserInput: boolean) => {
432-
if (isUserInput) {
433-
this._onChange(option.value);
441+
if (isUserInput && this._selected !== option) {
442+
this._emitChangeEvent(option);
434443
}
435444
this._onSelect(option);
436445
});
@@ -444,6 +453,16 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
444453
this._subscriptions = [];
445454
}
446455

456+
/** Emits an event when the user selects an option. */
457+
private _emitChangeEvent(option: MdOption): void {
458+
let event = new MdSelectChange();
459+
event.value = option.value;
460+
event.source = this;
461+
462+
this._onChange(option.value);
463+
this.change.emit(event);
464+
}
465+
447466
/** Records option IDs to pass to the aria-owns property. */
448467
private _setOptionIds() {
449468
this._optionIds = this.options.map(option => option.id).join(' ');

0 commit comments

Comments
 (0)