commit | author | age
|
2207d6
|
1 |
Migrating to typeahead.js v0.10.0 |
W |
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 :) |