| 15403 |
manish.sha |
1 |
Migrating to typeahead.js v0.10.0
|
|
|
2 |
=================================
|
|
|
3 |
|
|
|
4 |
Preamble
|
|
|
5 |
--------
|
|
|
6 |
|
|
|
7 |
v0.10.0 of typeahead.js ended up being almost a complete rewrite. Many things
|
|
|
8 |
stayed the same, but there were a handful of changes you need to be aware of
|
|
|
9 |
if you plan on upgrading from an older version. This document aims to call out
|
|
|
10 |
those changes and explain what you need to do in order to have an painless
|
|
|
11 |
upgrade.
|
|
|
12 |
|
|
|
13 |
Notable Changes
|
|
|
14 |
----------------
|
|
|
15 |
|
|
|
16 |
### First Argument to the jQuery Plugin
|
|
|
17 |
|
|
|
18 |
In v0.10.0, the first argument to `jQuery#typeahead` is an options hash that
|
|
|
19 |
can be used to configure the behavior of the typeahead. This is in contrast
|
|
|
20 |
to previous versions where `jQuery#typeahead` expected just a series of datasets
|
|
|
21 |
to be passed to it:
|
|
|
22 |
|
|
|
23 |
```javascript
|
|
|
24 |
// pre-v0.10.0
|
|
|
25 |
$('.typeahead').typeahead(myDataset);
|
|
|
26 |
|
|
|
27 |
// v0.10.0
|
|
|
28 |
$('.typeahead').typeahead({
|
|
|
29 |
highlight: true,
|
|
|
30 |
hint: false
|
|
|
31 |
}, myDataset);
|
|
|
32 |
```
|
|
|
33 |
|
|
|
34 |
If you're fine with the default configuration, you can just pass `null` as the
|
|
|
35 |
first argument:
|
|
|
36 |
|
|
|
37 |
```javascript
|
|
|
38 |
$('.typeahead').typeahead(null, myDataset);
|
|
|
39 |
```
|
|
|
40 |
|
|
|
41 |
### Bloodhound Suggestion Engine
|
|
|
42 |
|
|
|
43 |
The most notable change in v0.10.0 is that typeahead.js has been decomposed into
|
|
|
44 |
a suggestion engine and a UI view. As part of this change, the way you configure
|
|
|
45 |
datasets has changed. Previously, a dataset config would have looked like:
|
|
|
46 |
|
|
|
47 |
```javascript
|
|
|
48 |
{
|
|
|
49 |
valueKey: 'num',
|
|
|
50 |
local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],
|
|
|
51 |
prefetch: '/prefetch',
|
|
|
52 |
remote: '/remote?q=%QUERY'
|
|
|
53 |
}
|
|
|
54 |
```
|
|
|
55 |
|
|
|
56 |
In v0.10.0, an equivalent dataset config would look like:
|
|
|
57 |
|
|
|
58 |
```javascript
|
|
|
59 |
{
|
|
|
60 |
displayKey: 'num',
|
|
|
61 |
source: mySource
|
|
|
62 |
}
|
|
|
63 |
```
|
|
|
64 |
|
|
|
65 |
As you can see, `local`, `prefetch`, and `remote` are no longer defined at the
|
|
|
66 |
dataset level. Instead, all you set in a dataset config is `source`. `source` is
|
|
|
67 |
expected to be a function with the signature `function(query, callback)`. When a
|
|
|
68 |
typeahead's query changes, suggestions will be requested from `source`. It's
|
|
|
69 |
expected `source` will compute the suggestion set and invoke `callback` with an array
|
|
|
70 |
of suggestion objects. The typeahead will then go on to render those suggestions.
|
|
|
71 |
|
|
|
72 |
If you're wondering if you can still configure `local`, `prefetch`, and
|
|
|
73 |
`remote`, don't worry, that's where the Bloodhound suggestion engine comes in.
|
|
|
74 |
Here's how you would define `mySource` which was referenced in the previous
|
|
|
75 |
code snippet:
|
|
|
76 |
|
|
|
77 |
```
|
|
|
78 |
var mySource = new Bloodhound({
|
|
|
79 |
datumTokenizer: function(d) {
|
|
|
80 |
return Bloodhound.tokenizers.whitespace(d.num);
|
|
|
81 |
},
|
|
|
82 |
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
|
|
83 |
local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],
|
|
|
84 |
prefetch: '/prefetch',
|
|
|
85 |
remote: '/remote?q=%QUERY'
|
|
|
86 |
});
|
|
|
87 |
|
|
|
88 |
// this kicks off the loading and processing of local and prefetch data
|
|
|
89 |
// the suggestion engine will be useless until it is initialized
|
|
|
90 |
mySource.initialize();
|
|
|
91 |
```
|
|
|
92 |
|
|
|
93 |
In the above snippet, a Bloodhound suggestion engine is initialized and that's
|
|
|
94 |
what will be used as the source of your dataset. There's still one last thing
|
|
|
95 |
that needs to be done before you can use a Bloodhound suggestion engine as the
|
|
|
96 |
source of a dataset. Because datasets expect `source` to be function, the
|
|
|
97 |
Bloodhound instance needs to be wrapped in an adapter so it can meet that
|
|
|
98 |
expectation.
|
|
|
99 |
|
|
|
100 |
```
|
|
|
101 |
mySource = mySource.ttAdapter();
|
|
|
102 |
```
|
|
|
103 |
|
|
|
104 |
Put it all together:
|
|
|
105 |
|
|
|
106 |
```javascript
|
|
|
107 |
var mySource = new Bloodhound({
|
|
|
108 |
datumTokenizer: function(d) {
|
|
|
109 |
return Bloodhound.tokenizers.whitespace(d.num);
|
|
|
110 |
},
|
|
|
111 |
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
|
|
112 |
local: [{ num: 'one' }, { num: 'two' }, { num: 'three' }],
|
|
|
113 |
prefetch: '/prefetch',
|
|
|
114 |
remote: '/remote?q=%QUERY'
|
|
|
115 |
});
|
|
|
116 |
|
|
|
117 |
mySource.initialize();
|
|
|
118 |
|
|
|
119 |
$('.typeahead').typeahead(null, {
|
|
|
120 |
displayKey: 'num',
|
|
|
121 |
source: mySource.ttAdapter()
|
|
|
122 |
});
|
|
|
123 |
```
|
|
|
124 |
|
|
|
125 |
### Tokenization Methods Must Be Provided
|
|
|
126 |
|
|
|
127 |
The Bloodhound suggestion engine is token-based, so how datums and queries are
|
|
|
128 |
tokenized plays a vital role in the quality of search results. Pre-v0.10.0,
|
|
|
129 |
it was not possible to configure the tokenization method. Starting in v0.10.0,
|
|
|
130 |
you **must** specify how you want datums and queries tokenized.
|
|
|
131 |
|
|
|
132 |
The most common tokenization methods split a given string on whitespace or
|
|
|
133 |
non-word characters. Bloodhound provides implementations for those methods
|
|
|
134 |
out of the box:
|
|
|
135 |
|
|
|
136 |
```javascript
|
|
|
137 |
// returns ['one', 'two', 'twenty-five']
|
|
|
138 |
Bloodhound.tokenizers.whitespace(' one two twenty-five');
|
|
|
139 |
|
|
|
140 |
// returns ['one', 'two', 'twenty', 'five']
|
|
|
141 |
Bloodhound.tokenizers.nonword(' one two twenty-five');
|
|
|
142 |
```
|
|
|
143 |
|
|
|
144 |
For query tokenization, you'll probably want to use one of the above methods.
|
|
|
145 |
For datum tokenization, this is where you may want to do something a tad bit
|
|
|
146 |
more advanced.
|
|
|
147 |
|
|
|
148 |
For datums, sometimes you want tokens to be dervied from more than one property.
|
|
|
149 |
For example, if you were building a search engine for GitHub repositories, it'd
|
|
|
150 |
probably be wise to have tokens derived from the repo's name, owner, and
|
|
|
151 |
primary language:
|
|
|
152 |
|
|
|
153 |
```javascript
|
|
|
154 |
var repos = [
|
|
|
155 |
{ name: 'example', owner: 'John Doe', language: 'JavaScript' },
|
|
|
156 |
{ name: 'another example', owner: 'Joe Doe', language: 'Scala' }
|
|
|
157 |
];
|
|
|
158 |
|
|
|
159 |
function customTokenizer(datum) {
|
|
|
160 |
var nameTokens = Bloodhound.tokenizers.whitespace(datum.name);
|
|
|
161 |
var ownerTokens = Bloodhound.tokenizers.whitespace(datum.owner);
|
|
|
162 |
var languageTokens = Bloodhound.tokenizers.whitespace(datum.language);
|
|
|
163 |
|
|
|
164 |
return nameTokens.concat(ownerTokens).concat(languageTokens);
|
|
|
165 |
}
|
|
|
166 |
```
|
|
|
167 |
|
|
|
168 |
There may also be the scenario where you want datum tokenization to be performed
|
|
|
169 |
on the backend. The best way to do that is to just add a property to your datums
|
|
|
170 |
that contains those tokens. You can then provide a tokenizer that just returns
|
|
|
171 |
the already existing tokens:
|
|
|
172 |
|
|
|
173 |
```javascript
|
|
|
174 |
var sports = [
|
|
|
175 |
{ value: 'football', tokens: ['football', 'pigskin'] },
|
|
|
176 |
{ value: 'basketball', tokens: ['basketball', 'bball'] }
|
|
|
177 |
];
|
|
|
178 |
|
|
|
179 |
function customTokenizer(datum) { return datum.tokens; }
|
|
|
180 |
```
|
|
|
181 |
|
|
|
182 |
There are plenty of other ways you could go about tokenizing datums, it really
|
|
|
183 |
just depends on what you are trying to accomplish.
|
|
|
184 |
|
|
|
185 |
### String Datums Are No Longer Supported
|
|
|
186 |
|
|
|
187 |
Dropping support for string datums was a difficult choice, but in the end it
|
|
|
188 |
made sense for a number of reasons. If you still want to hydrate the suggestion
|
|
|
189 |
engine with string datums, you'll need to use the `filter` function:
|
|
|
190 |
|
|
|
191 |
```javascript
|
|
|
192 |
var engine = new Bloodhound({
|
|
|
193 |
prefetch: {
|
|
|
194 |
url: '/data',
|
|
|
195 |
filter: function(data) {
|
|
|
196 |
// assume data is an array of strings e.g. ['one', 'two', 'three']
|
|
|
197 |
return $.map(data, function(str) { return { value: str }; });
|
|
|
198 |
},
|
|
|
199 |
datumTokenizer: function(d) {
|
|
|
200 |
return Bloodhound.tokenizers.whitespace(d.value);
|
|
|
201 |
},
|
|
|
202 |
queryTokenizer: Bloodhound.tokenizers.whitespace
|
|
|
203 |
}
|
|
|
204 |
});
|
|
|
205 |
```
|
|
|
206 |
|
|
|
207 |
### Precompiled Templates Are Now Required
|
|
|
208 |
|
|
|
209 |
In previous versions of typeahead.js, you could specify a string template along
|
|
|
210 |
with the templating engine that should be used to compile/render it. In
|
|
|
211 |
v0.10.0, you can no longer specify templating engines; instead you must provide
|
|
|
212 |
precompiled templates. Precompiled templates are functions that take one
|
|
|
213 |
argument: the context the template should be rendered with.
|
|
|
214 |
|
|
|
215 |
Most of the popular templating engines allow for the creation of precompiled
|
|
|
216 |
templates. For example, you can generate one using Handlebars by doing the
|
|
|
217 |
following:
|
|
|
218 |
|
|
|
219 |
```javascript
|
|
|
220 |
var precompiledTemplate = Handlebars.compile('<p>{{value}}</p>');
|
|
|
221 |
```
|
|
|
222 |
|
|
|
223 |
[Handlebars]: http://handlebarsjs.com/
|
|
|
224 |
|
|
|
225 |
### CSS Class Changes
|
|
|
226 |
|
|
|
227 |
`tt-is-under-cursor` is now `tt-cursor` - Applied to a hovered-on suggestion (either via cursor or arrow key).
|
|
|
228 |
|
|
|
229 |
`tt-query` is now `tt-input` - Applied to the typeahead input field.
|
|
|
230 |
|
|
|
231 |
Something Missing?
|
|
|
232 |
------------------
|
|
|
233 |
|
|
|
234 |
If something is missing from this migration guide, pull requests are accepted :)
|