11import {
2- AfterContentInit , Directive , ElementRef , Input , ViewContainerRef , Optional , OnDestroy
2+ AfterContentInit ,
3+ Directive ,
4+ ElementRef ,
5+ forwardRef ,
6+ Input ,
7+ Optional ,
8+ OnDestroy ,
9+ ViewContainerRef ,
310} from '@angular/core' ;
4- import { NgControl } from '@angular/forms' ;
11+ import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
512import { Overlay , OverlayRef , OverlayState , TemplatePortal } from '../core' ;
613import { MdAutocomplete } from './autocomplete' ;
714import { PositionStrategy } from '../core/overlay/position/position-strategy' ;
@@ -28,6 +35,16 @@ export const AUTOCOMPLETE_OPTION_HEIGHT = 48;
2835/** The total height of the autocomplete panel. */
2936export const AUTOCOMPLETE_PANEL_HEIGHT = 256 ;
3037
38+ /**
39+ * Provider that allows the autocomplete to register as a ControlValueAccessor.
40+ * @docs -private
41+ */
42+ export const MD_AUTOCOMPLETE_VALUE_ACCESSOR : any = {
43+ provide : NG_VALUE_ACCESSOR ,
44+ useExisting : forwardRef ( ( ) => MdAutocompleteTrigger ) ,
45+ multi : true
46+ } ;
47+
3148@Directive ( {
3249 selector : 'input[mdAutocomplete], input[matAutocomplete]' ,
3350 host : {
@@ -39,10 +56,13 @@ export const AUTOCOMPLETE_PANEL_HEIGHT = 256;
3956 '[attr.aria-expanded]' : 'panelOpen.toString()' ,
4057 '[attr.aria-owns]' : 'autocomplete?.id' ,
4158 '(focus)' : 'openPanel()' ,
59+ '(blur)' : '_onTouched()' ,
60+ '(input)' : '_onChange($event.target.value)' ,
4261 '(keydown)' : '_handleKeydown($event)' ,
43- }
62+ } ,
63+ providers : [ MD_AUTOCOMPLETE_VALUE_ACCESSOR ]
4464} )
45- export class MdAutocompleteTrigger implements AfterContentInit , OnDestroy {
65+ export class MdAutocompleteTrigger implements AfterContentInit , ControlValueAccessor , OnDestroy {
4666 private _overlayRef : OverlayRef ;
4767 private _portal : TemplatePortal ;
4868 private _panelOpen : boolean = false ;
@@ -54,12 +74,18 @@ export class MdAutocompleteTrigger implements AfterContentInit, OnDestroy {
5474 private _keyManager : ActiveDescendantKeyManager ;
5575 private _positionStrategy : ConnectedPositionStrategy ;
5676
77+ /** View -> model callback called when value changes */
78+ _onChange : ( value : any ) => { } ;
79+
80+ /** View -> model callback called when autocomplete has been touched */
81+ _onTouched = ( ) => { } ;
82+
5783 /* The autocomplete panel to be attached to this trigger. */
5884 @Input ( 'mdAutocomplete' ) autocomplete : MdAutocomplete ;
5985
6086 constructor ( private _element : ElementRef , private _overlay : Overlay ,
6187 private _viewContainerRef : ViewContainerRef ,
62- @Optional ( ) private _controlDir : NgControl , @ Optional ( ) private _dir : Dir ) { }
88+ @Optional ( ) private _dir : Dir ) { }
6389
6490 ngAfterContentInit ( ) {
6591 this . _keyManager = new ActiveDescendantKeyManager ( this . autocomplete . options ) . withWrap ( ) ;
@@ -123,6 +149,38 @@ export class MdAutocompleteTrigger implements AfterContentInit, OnDestroy {
123149 return this . _keyManager . activeItem as MdOption ;
124150 }
125151
152+ /**
153+ * Sets the autocomplete's value. Part of the ControlValueAccessor interface
154+ * required to integrate with Angular's core forms API.
155+ *
156+ * @param value New value to be written to the model.
157+ */
158+ writeValue ( value : any ) : void {
159+ Promise . resolve ( null ) . then ( ( ) => this . _setTriggerValue ( value ) ) ;
160+ }
161+
162+ /**
163+ * Saves a callback function to be invoked when the autocomplete's value
164+ * changes from user input. Part of the ControlValueAccessor interface
165+ * required to integrate with Angular's core forms API.
166+ *
167+ * @param fn Callback to be triggered when the value changes.
168+ */
169+ registerOnChange ( fn : ( value : any ) => { } ) : void {
170+ this . _onChange = fn ;
171+ }
172+
173+ /**
174+ * Saves a callback function to be invoked when the autocomplete is blurred
175+ * by the user. Part of the ControlValueAccessor interface required
176+ * to integrate with Angular's core forms API.
177+ *
178+ * @param fn Callback to be triggered when the component has been touched.
179+ */
180+ registerOnTouched ( fn : ( ) => { } ) {
181+ this . _onTouched = fn ;
182+ }
183+
126184 _handleKeydown ( event : KeyboardEvent ) : void {
127185 if ( this . activeOption && event . keyCode === ENTER ) {
128186 this . activeOption . _selectViaInteraction ( ) ;
@@ -178,17 +236,20 @@ export class MdAutocompleteTrigger implements AfterContentInit, OnDestroy {
178236 }
179237 }
180238
239+ private _setTriggerValue ( value : any ) : void {
240+ this . _element . nativeElement . value =
241+ this . autocomplete . displayWith ? this . autocomplete . displayWith ( value ) : value ;
242+ }
243+
181244 /**
182245 * This method closes the panel, and if a value is specified, also sets the associated
183246 * control to that value. It will also mark the control as dirty if this interaction
184247 * stemmed from the user.
185248 */
186249 private _setValueAndClose ( event : MdOptionSelectEvent | null ) : void {
187250 if ( event ) {
188- this . _controlDir . control . setValue ( event . source . value ) ;
189- if ( event . isUserInput ) {
190- this . _controlDir . control . markAsDirty ( ) ;
191- }
251+ this . _setTriggerValue ( event . source . value ) ;
252+ this . _onChange ( event . source . value ) ;
192253 }
193254
194255 this . closePanel ( ) ;
0 commit comments