@@ -4,8 +4,10 @@ import {
44 ElementRef ,
55 forwardRef ,
66 Input ,
7+ NgZone ,
78 Optional ,
89 OnDestroy ,
10+ QueryList ,
911 ViewContainerRef ,
1012} from '@angular/core' ;
1113import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
@@ -18,6 +20,7 @@ import {MdOptionSelectEvent, MdOption} from '../core/option/option';
1820import { ActiveDescendantKeyManager } from '../core/a11y/activedescendant-key-manager' ;
1921import { ENTER , UP_ARROW , DOWN_ARROW } from '../core/keyboard/keycodes' ;
2022import { Subscription } from 'rxjs/Subscription' ;
23+ import 'rxjs/add/observable/of' ;
2124import 'rxjs/add/observable/merge' ;
2225import { Dir } from '../core/rtl/dir' ;
2326import 'rxjs/add/operator/startWith' ;
@@ -57,7 +60,7 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
5760 '[attr.aria-owns]' : 'autocomplete?.id' ,
5861 '(focus)' : 'openPanel()' ,
5962 '(blur)' : '_onTouched()' ,
60- '(input)' : '_onChange ($event.target.value)' ,
63+ '(input)' : '_handleInput ($event.target.value)' ,
6164 '(keydown)' : '_handleKeydown($event)' ,
6265 } ,
6366 providers : [ MD_AUTOCOMPLETE_VALUE_ACCESSOR ]
@@ -85,7 +88,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
8588
8689 constructor ( private _element : ElementRef , private _overlay : Overlay ,
8790 private _viewContainerRef : ViewContainerRef ,
88- @Optional ( ) private _dir : Dir ) { }
91+ @Optional ( ) private _dir : Dir , private _zone : NgZone ) { }
8992
9093 ngAfterContentInit ( ) {
9194 this . _keyManager = new ActiveDescendantKeyManager ( this . autocomplete . options ) . withWrap ( ) ;
@@ -131,7 +134,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
131134 * A stream of actions that should close the autocomplete panel, including
132135 * when an option is selected and when the backdrop is clicked.
133136 */
134- get panelClosingActions ( ) : Observable < any > {
137+ get panelClosingActions ( ) : Observable < MdOptionSelectEvent | null > {
135138 return Observable . merge (
136139 ...this . optionSelections ,
137140 this . _overlayRef . backdropClick ( ) ,
@@ -140,7 +143,7 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
140143 }
141144
142145 /** Stream of autocomplete option selections. */
143- get optionSelections ( ) : Observable < any > [ ] {
146+ get optionSelections ( ) : Observable < MdOptionSelectEvent > [ ] {
144147 return this . autocomplete . options . map ( option => option . onSelect ) ;
145148 }
146149
@@ -149,6 +152,11 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
149152 return this . _keyManager . activeItem as MdOption ;
150153 }
151154
155+ /** The initial list of autocomplete options, as soon as the zone has stabilized */
156+ get initialOptionList ( ) : Observable < QueryList < MdOption > > {
157+ return this . _zone . onStable . first ( ) . map ( ( ) => this . autocomplete . options ) ;
158+ }
159+
152160 /**
153161 * Sets the autocomplete's value. Part of the ControlValueAccessor interface
154162 * required to integrate with Angular's core forms API.
@@ -185,14 +193,19 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
185193 if ( this . activeOption && event . keyCode === ENTER ) {
186194 this . activeOption . _selectViaInteraction ( ) ;
187195 } else {
188- this . openPanel ( ) ;
189196 this . _keyManager . onKeydown ( event ) ;
190197 if ( event . keyCode === UP_ARROW || event . keyCode === DOWN_ARROW ) {
198+ this . openPanel ( ) ;
191199 this . _scrollToOption ( ) ;
192200 }
193201 }
194202 }
195203
204+ _handleInput ( value : string ) : void {
205+ this . _onChange ( value ) ;
206+ this . openPanel ( ) ;
207+ }
208+
196209 /**
197210 * Given that we are not actually focusing active options, we must manually adjust scroll
198211 * to reveal options below the fold. First, we find the offset of the option from the top
@@ -211,15 +224,15 @@ export class MdAutocompleteTrigger implements AfterContentInit, ControlValueAcce
211224 * stream every time the option list changes.
212225 */
213226 private _subscribeToClosingActions ( ) : void {
214- // Every time the option list changes...
215- this . autocomplete . options . changes
216- // and also at initialization, before there are any option changes...
217- . startWith ( null )
227+ // When the zone is stable initially, and when the option list changes...
228+ Observable . merge ( this . initialOptionList , this . autocomplete . options . changes )
218229 // create a new stream of panelClosingActions, replacing any previous streams
219230 // that were created, and flatten it so our stream only emits closing events...
220- . switchMap ( ( ) => {
231+ . switchMap ( options => {
221232 this . _resetPanel ( ) ;
222- return this . panelClosingActions ;
233+ // If the options list is empty, emit close event immediately.
234+ // Otherwise, listen for panel closing actions...
235+ return options . length ? this . panelClosingActions : Observable . of ( null ) ;
223236 } )
224237 // when the first closing event occurs...
225238 . first ( )
0 commit comments