Skip to content

Commit c6112aa

Browse files
committed
feat(autocomplete): add value support
1 parent 096b528 commit c6112aa

File tree

9 files changed

+217
-37
lines changed

9 files changed

+217
-37
lines changed
Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
<div class="demo-autocomplete">
2-
<md-input-container>
3-
<input mdInput placeholder="State" [mdAutocomplete]="auto">
4-
</md-input-container>
2+
<md-card>
3+
<md-input-container>
4+
<input mdInput placeholder="State" [mdAutocomplete]="auto" [formControl]="stateCtrl">
5+
</md-input-container>
56

6-
<md-autocomplete #auto="mdAutocomplete">
7-
<md-option *ngFor="let state of states" [value]="state.code"> {{ state.name }} </md-option>
8-
</md-autocomplete>
7+
<md-autocomplete #auto="mdAutocomplete">
8+
<md-option *ngFor="let state of filteredStates" [value]="state.code"> {{ state.name }} </md-option>
9+
</md-autocomplete>
10+
</md-card>
11+
<md-card>
12+
<div>Value: {{ stateCtrl.value }}</div>
13+
<div>Status: {{ stateCtrl.status }}</div>
14+
<div>Dirty: {{ stateCtrl.dirty }}</div>
15+
<md-card-actions>
16+
<button md-button (click)="stateCtrl.reset()">RESET</button>
17+
<button md-button (click)="stateCtrl.setValue('CA')">SET VALUE</button>
18+
<button md-button (click)="stateCtrl.enabled ? stateCtrl.disable() : stateCtrl.enable()">
19+
TOGGLE DISABLED
20+
</button>
21+
</md-card-actions>
22+
</md-card>
923
</div>
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
1-
.demo-autocomplete {}
1+
.demo-autocomplete {
2+
display: flex;
3+
flex-flow: row wrap;
4+
5+
md-card {
6+
width: 350px;
7+
margin: 24px;
8+
}
9+
}

src/demo-app/autocomplete/autocomplete-demo.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
import {Component} from '@angular/core';
1+
import {Component, OnDestroy} from '@angular/core';
2+
import {FormControl} from '@angular/forms';
3+
import {Subscription} from 'rxjs/Subscription';
4+
import 'rxjs/add/operator/debounceTime';
25

36
@Component({
47
moduleId: module.id,
58
selector: 'autocomplete-demo',
69
templateUrl: 'autocomplete-demo.html',
710
styleUrls: ['autocomplete-demo.css'],
811
})
9-
export class AutocompleteDemo {
12+
export class AutocompleteDemo implements OnDestroy {
13+
stateCtrl = new FormControl();
14+
filteredStates: any[];
15+
valueSub: Subscription;
1016
states = [
1117
{code: 'AL', name: 'Alabama'},
1218
{code: 'AZ', name: 'Arizona'},
@@ -35,4 +41,18 @@ export class AutocompleteDemo {
3541
{code: 'WI', name: 'Wisconsin'},
3642
{code: 'WY', name: 'Wyoming'},
3743
];
44+
45+
constructor() {
46+
this.filteredStates = this.states;
47+
this.valueSub = this.stateCtrl.valueChanges.debounceTime(100).subscribe((val) => {
48+
this.filteredStates = val ? this.states.filter((s) => s.name.match(new RegExp(val, 'gi')))
49+
: this.states;
50+
});
51+
52+
}
53+
54+
ngOnDestroy() {
55+
this.valueSub.unsubscribe();
56+
}
57+
3858
}

src/lib/autocomplete/_autocomplete-theme.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
$foreground: map-get($theme, foreground);
55
$background: map-get($theme, background);
66

7-
md-option {
7+
.md-autocomplete-panel {
88
background: md-color($background, card);
99
color: md-color($foreground, text);
10+
}
1011

12+
md-option {
1113
&.md-selected {
1214
background: md-color($background, card);
1315
color: md-color($foreground, text);

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import {Directive, ElementRef, Input, ViewContainerRef, OnDestroy} from '@angular/core';
1+
import {
2+
Directive, ElementRef, Input, ViewContainerRef, Optional, OnDestroy
3+
} from '@angular/core';
4+
import {NgControl} from '@angular/forms';
25
import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core';
36
import {MdAutocomplete} from './autocomplete';
47
import {PositionStrategy} from '../core/overlay/position/position-strategy';
58
import {Observable} from 'rxjs/Observable';
69
import {Subscription} from 'rxjs/Subscription';
710
import 'rxjs/add/observable/merge';
11+
import {MdOptionSelectEvent} from '../core/option/option';
812

913
/** The panel needs a slight y-offset to ensure the input underline displays. */
1014
export const MD_AUTOCOMPLETE_PANEL_OFFSET = 6;
@@ -20,12 +24,13 @@ export class MdAutocompleteTrigger implements OnDestroy {
2024
private _portal: TemplatePortal;
2125
private _panelOpen: boolean = false;
2226
private _closeWatcher: Subscription;
27+
private _optionWatcher: Subscription;
2328

2429
/* The autocomplete panel to be attached to this trigger. */
2530
@Input('mdAutocomplete') autocomplete: MdAutocomplete;
2631

2732
constructor(private _element: ElementRef, private _overlay: Overlay,
28-
private _vcr: ViewContainerRef) {}
33+
private _vcr: ViewContainerRef, @Optional() private _controlDir: NgControl) {}
2934

3035
ngOnDestroy() { this.destroyPanel(); }
3136

@@ -43,6 +48,8 @@ export class MdAutocompleteTrigger implements OnDestroy {
4348
if (!this._overlayRef.hasAttached()) {
4449
this._overlayRef.attach(this._portal);
4550
this._watchForClose();
51+
this._optionWatcher =
52+
this.autocomplete.options.changes.subscribe(() => this._watchForClose());
4653
}
4754

4855
this._panelOpen = true;
@@ -54,6 +61,7 @@ export class MdAutocompleteTrigger implements OnDestroy {
5461
this._overlayRef.detach();
5562
}
5663

64+
this._optionWatcher.unsubscribe();
5765
this._closeWatcher.unsubscribe();
5866
this._panelOpen = false;
5967
}
@@ -71,10 +79,13 @@ export class MdAutocompleteTrigger implements OnDestroy {
7179
* This method will close the panel if it receives a selection event from any of the options
7280
* or a click on the backdrop.
7381
*/
74-
private _watchForClose() {
82+
private _watchForClose(): void {
7583
// TODO(kara): add tab event watcher when adding keyboard events
84+
if (this._closeWatcher) {
85+
this._closeWatcher.unsubscribe();
86+
}
7687
this._closeWatcher = Observable.merge(...this._getOptionObs(), this._overlayRef.backdropClick())
77-
.subscribe(() => this.closePanel());
88+
.subscribe((event) => this._setValueAndClose(event));
7889
}
7990

8091
/**
@@ -86,6 +97,23 @@ export class MdAutocompleteTrigger implements OnDestroy {
8697
return this.autocomplete.options.map((option) => option.onSelect);
8798
}
8899

100+
/**
101+
* This method closes the panel, and if a value is specified, also sets the associated
102+
* control to that value. It will also mark the control as dirty if this interaction
103+
* stemmed from the user.
104+
*/
105+
private _setValueAndClose(event: MdOptionSelectEvent | null): void {
106+
if (event) {
107+
// TODO(kara): revisit animation once floating placeholder is toggle-able
108+
this._controlDir.control.setValue(event.source.viewValue);
109+
if (event.isUserInput) {
110+
this._controlDir.control.markAsDirty();
111+
}
112+
}
113+
114+
this.closePanel();
115+
}
116+
89117
private _createOverlay(): void {
90118
this._portal = new TemplatePortal(this.autocomplete.template, this._vcr);
91119
this._overlayRef = this._overlay.create(this._getOverlayConfig());

0 commit comments

Comments
 (0)