11import {
2- Directive ,
3- ElementRef ,
4- forwardRef ,
5- Host ,
6- Input ,
7- NgZone ,
8- Optional ,
9- OnDestroy ,
10- ViewContainerRef ,
2+ Directive ,
3+ ElementRef ,
4+ forwardRef ,
5+ Host ,
6+ Input ,
7+ NgZone ,
8+ Optional ,
9+ OnDestroy ,
10+ ViewContainerRef ,
11+ Renderer ,
12+ Inject ,
13+ ChangeDetectorRef ,
1114} from '@angular/core' ;
1215import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
16+ import { DOCUMENT } from '@angular/platform-browser' ;
1317import { Overlay , OverlayRef , OverlayState , TemplatePortal } from '../core' ;
1418import { MdAutocomplete } from './autocomplete' ;
1519import { PositionStrategy } from '../core/overlay/position/position-strategy' ;
@@ -18,12 +22,13 @@ import {Observable} from 'rxjs/Observable';
1822import { MdOptionSelectionChange , MdOption } from '../core/option/option' ;
1923import { ENTER , UP_ARROW , DOWN_ARROW } from '../core/keyboard/keycodes' ;
2024import { Dir } from '../core/rtl/dir' ;
25+ import { MdInputContainer } from '../input/input-container' ;
2126import { Subscription } from 'rxjs/Subscription' ;
22- import { Subject } from 'rxjs/Subject' ;
2327import 'rxjs/add/observable/merge' ;
28+ import 'rxjs/add/observable/fromEvent' ;
29+ import 'rxjs/add/operator/filter' ;
2430import 'rxjs/add/operator/startWith' ;
2531import 'rxjs/add/operator/switchMap' ;
26- import { MdInputContainer } from '../input/input-container' ;
2732
2833/**
2934 * The following style constants are necessary to save here in order
@@ -58,8 +63,8 @@ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
5863 '[attr.aria-expanded]' : 'panelOpen.toString()' ,
5964 '[attr.aria-owns]' : 'autocomplete?.id' ,
6065 '(focus)' : 'openPanel()' ,
61- '(blur)' : '_handleBlur($event.relatedTarget?.tagName)' ,
6266 '(input)' : '_handleInput($event)' ,
67+ '(blur)' : '_onTouched()' ,
6368 '(keydown)' : '_handleKeydown($event)' ,
6469 } ,
6570 providers : [ MD_AUTOCOMPLETE_VALUE_ACCESSOR ]
@@ -74,9 +79,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
7479
7580 private _positionStrategy : ConnectedPositionStrategy ;
7681
77- /** Stream of blur events that should close the panel. */
78- private _blurStream = new Subject < any > ( ) ;
79-
8082 /** Whether or not the placeholder state is being overridden. */
8183 private _manuallyFloatingPlaceholder = false ;
8284
@@ -100,9 +102,11 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
100102 }
101103
102104 constructor ( private _element : ElementRef , private _overlay : Overlay ,
103- private _viewContainerRef : ViewContainerRef ,
105+ private _renderer : Renderer , private _viewContainerRef : ViewContainerRef ,
106+ private _changeDetectorRef : ChangeDetectorRef ,
104107 @Optional ( ) private _dir : Dir , private _zone : NgZone ,
105- @Optional ( ) @Host ( ) private _inputContainer : MdInputContainer ) { }
108+ @Optional ( ) @Host ( ) private _inputContainer : MdInputContainer ,
109+ @Optional ( ) @Inject ( DOCUMENT ) private _document : any ) { }
106110
107111 ngOnDestroy ( ) {
108112 if ( this . _panelPositionSubscription ) {
@@ -144,6 +148,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
144148
145149 this . _panelOpen = false ;
146150 this . _resetPlaceholder ( ) ;
151+ this . _changeDetectorRef . detectChanges ( ) ;
147152 }
148153
149154 /**
@@ -152,9 +157,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
152157 */
153158 get panelClosingActions ( ) : Observable < MdOptionSelectionChange > {
154159 return Observable . merge (
155- this . optionSelections ,
156- this . _blurStream . asObservable ( ) ,
157- this . autocomplete . _keyManager . tabOut
160+ this . optionSelections ,
161+ this . autocomplete . _keyManager . tabOut ,
162+ this . outsideClickStream
158163 ) ;
159164 }
160165
@@ -170,6 +175,17 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
170175 }
171176 }
172177
178+ get outsideClickStream ( ) : Observable < any > {
179+ if ( this . _document ) {
180+ return Observable . fromEvent ( this . _document , 'click' ) . filter ( ( event : MouseEvent ) => {
181+ let clickTarget = event . target as HTMLElement ;
182+ return this . _panelOpen &&
183+ ! this . _inputContainer . _elementRef . nativeElement . contains ( clickTarget ) &&
184+ ! this . _overlayRef . overlayElement . contains ( clickTarget ) ;
185+ } ) ;
186+ }
187+ }
188+
173189 /**
174190 * Sets the autocomplete's value. Part of the ControlValueAccessor interface
175191 * required to integrate with Angular's core forms API.
@@ -225,15 +241,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
225241 }
226242 }
227243
228- _handleBlur ( newlyFocusedTag : string ) : void {
229- this . _onTouched ( ) ;
230-
231- // Only emit blur event if the new focus is *not* on an option.
232- if ( newlyFocusedTag !== 'MD-OPTION' ) {
233- this . _blurStream . next ( null ) ;
234- }
235- }
236-
237244 /**
238245 * In "auto" mode, the placeholder will animate down as soon as focus is lost.
239246 * This causes the value to jump when selecting an option with the mouse.
@@ -307,7 +314,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
307314 * stemmed from the user.
308315 */
309316 private _setValueAndClose ( event : MdOptionSelectionChange | null ) : void {
310- if ( event ) {
317+ if ( event && event . source ) {
311318 this . _clearPreviousSelectedOption ( event . source ) ;
312319 this . _setTriggerValue ( event . source . value ) ;
313320 this . _onChange ( event . source . value ) ;
0 commit comments