Skip to content

Instantly share code, notes, and snippets.

@maggiben
Created July 19, 2015 07:59
Show Gist options
  • Save maggiben/560a9f045a138b3ed578 to your computer and use it in GitHub Desktop.
Save maggiben/560a9f045a138b3ed578 to your computer and use it in GitHub Desktop.
A Reactive autocomplete for Aurelia
import {inject, customAttribute, bindable} from 'aurelia-framework';
import {HttpClient} from 'aurelia-http-client';
import typeahead from 'typeahead';
import Rx from 'rx';
import 'styles/type-ahead.css!'
@inject(Element, HttpClient)
@bindable("value")
@customAttribute('searcher')
export class Searcher {
isSearching = false;
constructor(element, http) {
this.element = element;
this.http = http;
}
searchTerm(term) {
return new HttpClient().createRequest('api/v2/Lookup/jsonp')
.asJsonp()
.withBaseUrl('http://dev.markitondemand.com')
.withParams({
input: term
})
.withCallbackParameterName('callback')
.send()
.then(function(result) {
return result.response.map(function(item, index){
return {
symbol: item.Symbol,
name: item.Name,
exchange: item.Exchange
};
});
});
}
getSuggestions() {
//var keyup = Rx.Observable.fromEvent($(this.element).find('input'), 'keyup')
var asyncrequest = Rx.Observable.fromEvent($(this.element), 'typeahead:asyncrequest')
.map(function (e) {
return e.target.value; // Project the text from the input
})
.filter(function (text) {
var regex = new RegExp("^[a-zA-Z0-9_ ]*$"); // Match only alphanumric underscore and space
return regex.test(text);
})
.debounce(500) // debounce events
.distinctUntilChanged(); // Only if the value has changed
this.searcher = asyncrequest.flatMapLatest(this.searchTerm);
return (query, syncResults, asyncResults) => {
var subscription = this.searcher.first().subscribe(
(data) => {
return asyncResults(data);
},
(error) => {
return asyncResults([]);
}
);
};
}
bind() {
console.log("bind: ", this.value);
}
valueChanged(newValue) {
console.log("valueChanged: ", newValue);
}
attached() {
this.typeAhead = $(this.element).typeahead({
hint: true,
async: true,
highlight: true,
minLength: 2,
display: 'symbol',
classNames: {
menu: 'results'
}
},
{
name: 'tickers',
//valueKey: 'name',
limit: 4,
displayKey: 'symbol',
source: this.getSuggestions(),
templates: {
notFound: [
'<div class="empty-message">',
'unable to find any symbols that match the current query',
'</div>'
].join('\n'),
suggestion: data => {
return `<div><strong class="symbol">${data.symbol}</strong><span class="name">${data.name}</span><span class="exchange">${data.exchange}</span></div>`;
}
}
});
this.typeAhead.bind("typeahead:selected", (event, datum, name) => {
this.value = datum.symbol;
});
this.typeAhead.bind("typeahead:autocomplete", (event, datum, name) => {
this.value = datum.symbol;
});
// Typeahead will trigger an 'open' if input gains focus to display suggestions
// This in turn triggers antoher async call
this.typeAhead.bind('typeahead:asyncrequest', (event, query, name) => {
this.isSearching = true;
});
this.typeAhead.bind('typeahead:asyncreceive', (event, query, name) => {
this.isSearching = false;
});
this.typeAhead.bind('typeahead:asynccancel', (event, query, name) => {
this.isSearching = false;
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment