Skip to content

Commit 2dd5c7c

Browse files
crisbetokara
authored andcommitted
feat(autocomplete): emit event when an option is selected (#4187)
Fixes #4094. Fixes #3645.
1 parent 844a81c commit 2dd5c7c

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
427427
this._setTriggerValue(event.source.value);
428428
this._onChange(event.source.value);
429429
this._element.nativeElement.focus();
430+
this.autocomplete._emitSelectEvent(event.source);
430431
}
431432

432433
this.closePanel();

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
MdAutocomplete,
2222
MdAutocompleteModule,
2323
MdAutocompleteTrigger,
24+
MdAutocompleteSelectedEvent,
2425
} from './index';
2526
import {MdInputModule} from '../input/index';
2627
import {Subscription} from 'rxjs/Subscription';
@@ -57,7 +58,8 @@ describe('MdAutocomplete', () => {
5758
AutocompleteWithNativeInput,
5859
AutocompleteWithoutPanel,
5960
AutocompleteWithFormsAndNonfloatingPlaceholder,
60-
AutocompleteWithGroups
61+
AutocompleteWithGroups,
62+
AutocompleteWithSelectEvent,
6163
],
6264
providers: [
6365
{provide: OverlayContainer, useFactory: () => {
@@ -1548,6 +1550,55 @@ describe('MdAutocomplete', () => {
15481550
expect(panel.classList).toContain(visibleClass, `Expected panel to be visible.`);
15491551
});
15501552
}));
1553+
1554+
it('should emit an event when an option is selected', fakeAsync(() => {
1555+
let fixture = TestBed.createComponent(AutocompleteWithSelectEvent);
1556+
1557+
fixture.detectChanges();
1558+
fixture.componentInstance.trigger.openPanel();
1559+
tick();
1560+
fixture.detectChanges();
1561+
1562+
let options = overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
1563+
let spy = fixture.componentInstance.optionSelected;
1564+
1565+
options[1].click();
1566+
tick();
1567+
fixture.detectChanges();
1568+
1569+
expect(spy).toHaveBeenCalledTimes(1);
1570+
1571+
let event = spy.calls.mostRecent().args[0] as MdAutocompleteSelectedEvent;
1572+
1573+
expect(event.source).toBe(fixture.componentInstance.autocomplete);
1574+
expect(event.option.value).toBe('Washington');
1575+
}));
1576+
1577+
it('should emit an event when a newly-added option is selected', fakeAsync(() => {
1578+
let fixture = TestBed.createComponent(AutocompleteWithSelectEvent);
1579+
1580+
fixture.detectChanges();
1581+
fixture.componentInstance.trigger.openPanel();
1582+
tick();
1583+
fixture.detectChanges();
1584+
1585+
fixture.componentInstance.states.push('Puerto Rico');
1586+
fixture.detectChanges();
1587+
1588+
let options = overlayContainerElement.querySelectorAll('md-option') as NodeListOf<HTMLElement>;
1589+
let spy = fixture.componentInstance.optionSelected;
1590+
1591+
options[3].click();
1592+
tick();
1593+
fixture.detectChanges();
1594+
1595+
expect(spy).toHaveBeenCalledTimes(1);
1596+
1597+
let event = spy.calls.mostRecent().args[0] as MdAutocompleteSelectedEvent;
1598+
1599+
expect(event.source).toBe(fixture.componentInstance.autocomplete);
1600+
expect(event.option.value).toBe('Puerto Rico');
1601+
}));
15511602
});
15521603

15531604
@Component({
@@ -1826,3 +1877,25 @@ class AutocompleteWithGroups {
18261877
}
18271878
];
18281879
}
1880+
1881+
@Component({
1882+
template: `
1883+
<md-input-container>
1884+
<input mdInput placeholder="State" [mdAutocomplete]="auto" [(ngModel)]="selectedState">
1885+
</md-input-container>
1886+
1887+
<md-autocomplete #auto="mdAutocomplete" (optionSelected)="optionSelected($event)">
1888+
<md-option *ngFor="let state of states" [value]="state">
1889+
<span>{{ state }}</span>
1890+
</md-option>
1891+
</md-autocomplete>
1892+
`
1893+
})
1894+
class AutocompleteWithSelectEvent {
1895+
selectedState: string;
1896+
states = ['New York', 'Washington', 'Oregon'];
1897+
optionSelected = jasmine.createSpy('optionSelected callback');
1898+
1899+
@ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
1900+
@ViewChild(MdAutocomplete) autocomplete: MdAutocomplete;
1901+
}

src/lib/autocomplete/autocomplete.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,25 @@ import {
1818
ViewEncapsulation,
1919
ChangeDetectorRef,
2020
ChangeDetectionStrategy,
21+
EventEmitter,
22+
Output,
2123
} from '@angular/core';
2224
import {MdOption, MdOptgroup} from '../core';
2325
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
2426

27+
2528
/**
2629
* Autocomplete IDs need to be unique across components, so this counter exists outside of
2730
* the component definition.
2831
*/
2932
let _uniqueAutocompleteIdCounter = 0;
3033

34+
/** Event object that is emitted when an autocomplete option is selected */
35+
export class MdAutocompleteSelectedEvent {
36+
constructor(public source: MdAutocomplete, public option: MdOption) { }
37+
}
38+
39+
3140
@Component({
3241
moduleId: module.id,
3342
selector: 'md-autocomplete, mat-autocomplete',
@@ -63,6 +72,10 @@ export class MdAutocomplete implements AfterContentInit {
6372
/** Function that maps an option's control value to its display value in the trigger. */
6473
@Input() displayWith: ((value: any) => string) | null = null;
6574

75+
/** Event that is emitted whenever an option from the list is selected. */
76+
@Output() optionSelected: EventEmitter<MdAutocompleteSelectedEvent> =
77+
new EventEmitter<MdAutocompleteSelectedEvent>();
78+
6679
/** Unique ID to be used by autocomplete trigger's "aria-owns" property. */
6780
id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`;
6881

@@ -88,13 +101,19 @@ export class MdAutocomplete implements AfterContentInit {
88101
}
89102

90103
/** Panel should hide itself when the option list is empty. */
91-
_setVisibility() {
104+
_setVisibility(): void {
92105
Promise.resolve().then(() => {
93106
this.showPanel = !!this.options.length;
94107
this._changeDetectorRef.markForCheck();
95108
});
96109
}
97110

111+
/** Emits the `select` event. */
112+
_emitSelectEvent(option: MdOption): void {
113+
const event = new MdAutocompleteSelectedEvent(this, option);
114+
this.optionSelected.emit(event);
115+
}
116+
98117
/** Sets a class on the panel based on whether it is visible. */
99118
_getClassList() {
100119
return {

0 commit comments

Comments
 (0)