Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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 :)