mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(select): emit change event (#2458)
* feat(select): emit change event Adds an event to `md-select` that is emitted when the selected option has changed. Fixes #2248. * Separate the change event example from the ones that use the form directives. * Remove leftover event listener. * More leftovers.
This commit is contained in:
committed by
tinayuangao
parent
52aa71584f
commit
e5bd15cc6b
@@ -16,7 +16,6 @@
|
||||
</md-card>
|
||||
</div>
|
||||
|
||||
|
||||
<md-card>
|
||||
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="isRequired" [disabled]="isDisabled"
|
||||
#drinkControl="ngModel">
|
||||
@@ -34,5 +33,15 @@
|
||||
<button md-button (click)="drinkControl.reset()">RESET</button>
|
||||
</md-card>
|
||||
|
||||
<div *ngIf="showSelect">
|
||||
<md-card>
|
||||
<md-select placeholder="Starter Pokemon" (change)="latestChangeEvent = $event">
|
||||
<md-option *ngFor="let starter of pokemon" [value]="starter.value"> {{ starter.viewValue }} </md-option>
|
||||
</md-select>
|
||||
|
||||
<p> Change event value: {{ latestChangeEvent?.value }} </p>
|
||||
</md-card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="height: 500px">This div is for testing scrolled selects.</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {FormControl} from '@angular/forms';
|
||||
import {MdSelectChange} from '@angular/material';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@@ -12,6 +13,7 @@ export class SelectDemo {
|
||||
isDisabled = false;
|
||||
showSelect = false;
|
||||
currentDrink: string;
|
||||
latestChangeEvent: MdSelectChange;
|
||||
foodControl = new FormControl('pizza-1');
|
||||
|
||||
foods = [
|
||||
@@ -32,8 +34,13 @@ export class SelectDemo {
|
||||
{value: 'milk-8', viewValue: 'Milk'},
|
||||
];
|
||||
|
||||
pokemon = [
|
||||
{value: 'bulbasaur-0', viewValue: 'Bulbasaur'},
|
||||
{value: 'charizard-1', viewValue: 'Charizard'},
|
||||
{value: 'squirtle-2', viewValue: 'Squirtle'}
|
||||
];
|
||||
|
||||
toggleDisabled() {
|
||||
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('MdSelect', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MdSelectModule.forRoot(), ReactiveFormsModule, FormsModule],
|
||||
declarations: [BasicSelect, NgModelSelect, ManySelects, NgIfSelect],
|
||||
declarations: [BasicSelect, NgModelSelect, ManySelects, NgIfSelect, SelectWithChangeEvent],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
overlayContainerElement = document.createElement('div') as HTMLElement;
|
||||
@@ -1155,6 +1155,38 @@ describe('MdSelect', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('change event', () => {
|
||||
let fixture: ComponentFixture<SelectWithChangeEvent>;
|
||||
let trigger: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SelectWithChangeEvent);
|
||||
fixture.detectChanges();
|
||||
|
||||
trigger = fixture.debugElement.query(By.css('.md-select-trigger')).nativeElement;
|
||||
});
|
||||
|
||||
it('should emit an event when the selected option has changed', () => {
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
(overlayContainerElement.querySelector('md-option') as HTMLElement).click();
|
||||
|
||||
expect(fixture.componentInstance.changeListener).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit multiple change events for the same option', () => {
|
||||
trigger.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
let option = overlayContainerElement.querySelector('md-option') as HTMLElement;
|
||||
|
||||
option.click();
|
||||
option.click();
|
||||
|
||||
expect(fixture.componentInstance.changeListener).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
@@ -1249,7 +1281,28 @@ class NgIfSelect {
|
||||
@ViewChild(MdSelect) select: MdSelect;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'select-with-change-event',
|
||||
template: `
|
||||
<md-select (change)="changeListener($event)">
|
||||
<md-option *ngFor="let food of foods" [value]="food">{{ food }}</md-option>
|
||||
</md-select>
|
||||
`
|
||||
})
|
||||
class SelectWithChangeEvent {
|
||||
foods: string[] = [
|
||||
'steak-0',
|
||||
'pizza-1',
|
||||
'tacos-2',
|
||||
'sandwich-3',
|
||||
'chips-4',
|
||||
'eggs-5',
|
||||
'pasta-6',
|
||||
'sushi-7'
|
||||
];
|
||||
|
||||
changeListener = jasmine.createSpy('MdSelect change listener');
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Move this to core testing utility until Angular has event faking
|
||||
|
||||
@@ -64,6 +64,11 @@ export const SELECT_PANEL_PADDING_Y = 16;
|
||||
*/
|
||||
export const SELECT_PANEL_VIEWPORT_PADDING = 8;
|
||||
|
||||
/** Change event object that is emitted when the select value has changed. */
|
||||
export class MdSelectChange {
|
||||
constructor(public source: MdSelect, public value: any) { }
|
||||
}
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'md-select, mat-select',
|
||||
@@ -217,10 +222,13 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
set required(value: any) { this._required = coerceBooleanProperty(value); }
|
||||
|
||||
/** Event emitted when the select has been opened. */
|
||||
@Output() onOpen = new EventEmitter();
|
||||
@Output() onOpen: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
/** Event emitted when the select has been closed. */
|
||||
@Output() onClose = new EventEmitter();
|
||||
@Output() onClose: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
/** Event emitted when the selected value has been changed by the user. */
|
||||
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
|
||||
|
||||
constructor(private _element: ElementRef, private _renderer: Renderer,
|
||||
private _viewportRuler: ViewportRuler, @Optional() private _dir: Dir,
|
||||
@@ -434,8 +442,8 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
private _listenToOptions(): void {
|
||||
this.options.forEach((option: MdOption) => {
|
||||
const sub = option.onSelect.subscribe((isUserInput: boolean) => {
|
||||
if (isUserInput) {
|
||||
this._onChange(option.value);
|
||||
if (isUserInput && this._selected !== option) {
|
||||
this._emitChangeEvent(option);
|
||||
}
|
||||
this._onSelect(option);
|
||||
});
|
||||
@@ -449,6 +457,12 @@ export class MdSelect implements AfterContentInit, ControlValueAccessor, OnDestr
|
||||
this._subscriptions = [];
|
||||
}
|
||||
|
||||
/** Emits an event when the user selects an option. */
|
||||
private _emitChangeEvent(option: MdOption): void {
|
||||
this._onChange(option.value);
|
||||
this.change.emit(new MdSelectChange(this, option.value));
|
||||
}
|
||||
|
||||
/** Records option IDs to pass to the aria-owns property. */
|
||||
private _setOptionIds() {
|
||||
this._optionIds = this.options.map(option => option.id).join(' ');
|
||||
|
||||
Reference in New Issue
Block a user