Remote autocomplete filtering
The autocomplete filtering capabilities aren't just limited to the default implementation, which searches for objects in array sources. We can specify a custom source()
function that will retrieve only data the user is looking for. This is the ideal approach if you're looking to use autocomplete on a data source with thousands of items. Otherwise, filtering gets too demanding on the browser—the large data set to download, followed by a large array search with each keystroke.
How to do it...
We'll use the GitHub API as the data source for our autocomplete widget. This is a good example since it is much too large to use in the browser memory.
$( function() { $( "#autocomplete" ).autocomplete({ minLength: 3, source: function( request, response ) { $.ajax({ url: "https://api.github.com/legacy/repos/search/:" + request.term, dataType: "jsonp", success: function( resp ) { var repositories = resp.data.repositories.splice( 0, 10 ); var items = $.map( repositories, function ( item ) { return { label: item.name + " (" + item.language + ")", value: item.name }; }); response( items ); } }); } }); });
Now if you look at the resulting widget in the browser and start typing, you'll see Github repository data in the drop-down menu.
How it works...
Since we're using a large data source, we're telling this particular autocomplete widget that the search for items should only be performed if there are at least three characters. This is done using the minLength
option. Otherwise, we would be asking the server to query based on one or two characters which isn't what we're after.
The source
option in our example specifies the data source that we're going to use – the Github API. The function we've passed to the source performs an $.ajax()
call against the Github API. We're using jsonp
as the format, which simply means that a callback function from the API will be sent back. We're also passing some query data to the API.
Our success callback function is executed once the API responds with data. We then pass this data through the $.map()
utility function in order to produce an array the autocomplete widget understands. Our success function does a simple $.map()
on the data to transform it into an array of objects that the autocomplete can use.
There's more...
We can further cut back on the cost of network communication overheads by introducing a term cache to the widget. A term cache, as the name suggests, would store locally the results of performing a remote filter operation. This way, when the user inevitably does the exact same thing with their keystrokes, we're not performing the exact same task with the remote API call since we've already cached the result in the widget.
( function( $, undefined ) { $.widget( "ab.autocomplete", $.ui.autocomplete, { _cache: {}, _search: function( value ) { var response = this._response(), cache = this._cache; this.pending++; this.element.addClass( "ui-autocomplete-loading" ); this.cancelSearch = false; if ( value in cache ) { response( cache[value] ); } else { this.source( { term: value }, response ); } } }); })( jQuery ); $( function() { $( "#autocomplete" ).autocomplete({ minLength: 3, source: function( request, response ) { var self = this; $.ajax({ url: "https://api.github.com/legacy/repos/search/:" + request.term, dataType: "jsonp", success: function( resp ) { var repositories = resp.data.repositories.splice( 0, 10 ); var items = $.map( repositories, function ( item ) { return { label: item.name + " (" + item.language + ")", value: item.name }; }); self._cache[request.term] = items; response( items ); } }); } }); });
You can see where we've made changes in the preceding code to support caching the items returned from the HTTP request. Now we're extending the widget to add the new _cache
attribute. We're also extending the _search()
function, which is in charge of checking for a cached value. If it finds one, the rendering response is called using the cached version of the data. The source()
function is responsible for storing cached results, but this is a simple one-liner.