Skip to content

Commit

Permalink
feat(autocomplete): add value support
Browse files Browse the repository at this point in the history
  • Loading branch information
kara committed Jan 4, 2017
1 parent 76ce131 commit 902d72b
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 37 deletions.
26 changes: 20 additions & 6 deletions src/demo-app/autocomplete/autocomplete-demo.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
<div class="demo-autocomplete">
<md-input-container>
<input md-input placeholder="State" [mdAutocomplete]="auto">
</md-input-container>
<md-card>
<md-input-container>
<input md-input placeholder="State" [mdAutocomplete]="auto" [formControl]="stateCtrl">
</md-input-container>

<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let state of states" [value]="state.code"> {{ state.name }} </md-option>
</md-autocomplete>
<md-autocomplete #auto="mdAutocomplete">
<md-option *ngFor="let state of filteredStates" [value]="state.code"> {{ state.name }} </md-option>
</md-autocomplete>
</md-card>
<md-card>
<div>Value: {{ stateCtrl.value }}</div>
<div>Status: {{ stateCtrl.status }}</div>
<div>Dirty: {{ stateCtrl.dirty }}</div>
<md-card-actions>
<button md-button (click)="stateCtrl.reset()">RESET</button>
<button md-button (click)="stateCtrl.setValue('CA')">SET VALUE</button>
<button md-button (click)="stateCtrl.enabled ? stateCtrl.disable() : stateCtrl.enable()">
TOGGLE DISABLED
</button>
</md-card-actions>
</md-card>
</div>
10 changes: 9 additions & 1 deletion src/demo-app/autocomplete/autocomplete-demo.scss
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
.demo-autocomplete {}
.demo-autocomplete {
display: flex;
flex-flow: row wrap;

md-card {
width: 350px;
margin: 24px;
}
}
24 changes: 22 additions & 2 deletions src/demo-app/autocomplete/autocomplete-demo.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import {Component} from '@angular/core';
import {Component, OnDestroy} from '@angular/core';
import {FormControl} from '@angular/forms';
import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/operator/debounceTime';

@Component({
moduleId: module.id,
selector: 'autocomplete-demo',
templateUrl: 'autocomplete-demo.html',
styleUrls: ['autocomplete-demo.css'],
})
export class AutocompleteDemo {
export class AutocompleteDemo implements OnDestroy {
stateCtrl = new FormControl();
filteredStates: any[];
valueSub: Subscription;
states = [
{code: 'AL', name: 'Alabama'},
{code: 'AZ', name: 'Arizona'},
Expand Down Expand Up @@ -35,4 +41,18 @@ export class AutocompleteDemo {
{code: 'WI', name: 'Wisconsin'},
{code: 'WY', name: 'Wyoming'},
];

constructor() {
this.filteredStates = this.states;
this.valueSub = this.stateCtrl.valueChanges.debounceTime(100).subscribe((val) => {
this.filteredStates = val ? this.states.filter((s) => s.name.match(new RegExp(val, 'gi')))
: this.states;
});

}

ngOnDestroy() {
this.valueSub.unsubscribe();
}

}
4 changes: 3 additions & 1 deletion src/lib/autocomplete/_autocomplete-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);

md-option {
.md-autocomplete-panel {
background: md-color($background, card);
color: md-color($foreground, text);
}

md-option {
&.md-selected {
background: md-color($background, card);
color: md-color($foreground, text);
Expand Down
36 changes: 32 additions & 4 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {Directive, ElementRef, Input, ViewContainerRef, OnDestroy} from '@angular/core';
import {
Directive, ElementRef, Input, ViewContainerRef, Optional, OnDestroy
} from '@angular/core';
import {NgControl} from '@angular/forms';
import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core';
import {MdAutocomplete} from './autocomplete';
import {PositionStrategy} from '../core/overlay/position/position-strategy';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/observable/merge';
import {MdOptionSelectEvent} from '../core/option/option';

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

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

constructor(private _element: ElementRef, private _overlay: Overlay,
private _vcr: ViewContainerRef) {}
private _vcr: ViewContainerRef, @Optional() private _controlDir: NgControl) {}

ngOnDestroy() { this.destroyPanel(); }

Expand All @@ -43,6 +48,8 @@ export class MdAutocompleteTrigger implements OnDestroy {
if (!this._overlayRef.hasAttached()) {
this._overlayRef.attach(this._portal);
this._watchForClose();
this._optionWatcher =
this.autocomplete.options.changes.subscribe(() => this._watchForClose());
}

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

this._optionWatcher.unsubscribe();
this._closeWatcher.unsubscribe();
this._panelOpen = false;
}
Expand All @@ -71,10 +79,13 @@ export class MdAutocompleteTrigger implements OnDestroy {
* This method will close the panel if it receives a selection event from any of the options
* or a click on the backdrop.
*/
private _watchForClose() {
private _watchForClose(): void {
// TODO(kara): add tab event watcher when adding keyboard events
if (this._closeWatcher) {
this._closeWatcher.unsubscribe();
}
this._closeWatcher = Observable.merge(...this._getOptionObs(), this._overlayRef.backdropClick())
.subscribe(() => this.closePanel());
.subscribe((event) => this._setValueAndClose(event));
}

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

/**
* This method closes the panel, and if a value is specified, also sets the associated
* control to that value. It will also mark the control as dirty if this interaction
* stemmed from the user.
*/
private _setValueAndClose(event: MdOptionSelectEvent | null): void {
if (event) {
// TODO(kara): revisit animation once floating placeholder is toggle-able
this._controlDir.control.setValue(event.source.viewValue);
if (event.isUserInput) {
this._controlDir.control.markAsDirty();
}
}

this.closePanel();
}

private _createOverlay(): void {
this._portal = new TemplatePortal(this.autocomplete.template, this._vcr);
this._overlayRef = this._overlay.create(this._getOverlayConfig());
Expand Down
Loading

0 comments on commit 902d72b

Please sign in to comment.