meteor-typeahead
Twitter's typeahead.js autocomplete package, wrapped for Meteor 1.0+. Issue command meteor add sergeyt:typeahead
to install the package.
Initializing typeahead
When the DOM is loaded through Meteor.startup on each template
1Meteor.startup(function() { 2 Meteor.typeahead.inject(); 3});
with iron:router
Using iron:router the Meteor.startup is already triggered because it loads the template or the loading template and then inject the data. It must be delayed to when iron:router knows it is rendered completely.
1Template.demo.rendered = function() { 2 Meteor.typeahead.inject(); 3};
Examples
See demo application in this repository to find more examples.
data-source attribute
1<input class="form-control typeahead" name="team" type="text" 2 placeholder="NBA teams" 3 autocomplete="off" spellcheck="off" 4 data-source="nba"/>
1Nba = new Meteor.Collection("nba"); 2 3if (Meteor.isServer){ 4 Nba.insert({name:'Boston Celtics'}); 5 // fill Nba collection 6} 7 8Template.demo.helpers({ 9 nba: function() { 10 return Nba.find().fetch().map(function(it){ return it.name; }); 11 } 12});
Multiple datasets
1<template name="demo"> 2 <div class="form-group"> 3 <input class="form-control typeahead" name="team" type="text" 4 placeholder="NBA and NHL teams" 5 autocomplete="off" spellcheck="off" 6 data-sets="teams"/> 7 </div> 8</template> 9 10<template name="team"> 11 <h4><i>{{name}}</i></h4> 12</template>
1Template.demo.helpers({ 2 teams: function() { 3 return [ 4 { 5 name: 'nba-teams', 6 valueKey: 'name', 7 local: function() { return Nba.find().fetch(); }, 8 header: '<h3 class="league-name">NBA Teams</h3>', 9 template: 'team' 10 }, 11 { 12 name: 'nhl-teams', 13 valueKey: 'name', 14 local: function() { return Nhl.find().fetch(); }, 15 header: '<h3 class="league-name">NHL Teams</h3>', 16 template: 'team' 17 } 18 ]; 19 } 20});
Custom template to render suggestion
1<input class="form-control typeahead" name="repo" type="text" 2 placeholder="open source projects by Twitter" 3 autocomplete="off" spellcheck="off" 4 data-source="repos" data-template="repo"/> 5 6<template name="repo"> 7 <p class="repo-language">{{language}}</p> 8 <p class="repo-name">{{name}}</p> 9 <p class="repo-description">{{description}}</p> 10</template>
1Repos = new Meteor.Collection("repos"); 2 3if (Meteor.isServer){ 4 Meteor.startup(function(){ 5 Repos.remove({}); 6 // fill repos from private repos.json asset 7 JSON.parse(Assets.getText('repos.json')).forEach(function(it){ 8 Repos.insert(it); 9 }); 10 }); 11} 12 13if (Meteor.isClient){ 14 Template.demo.helpers({ 15 repos: function() { 16 // this only works if returned objects have 17 // an attribute named "value" containing the text 18 // See docs for "data-value-key" attribute 19 return Repos.find().fetch(); 20 } 21 }); 22}
Server side search
1<input class="form-control typeahead" name="search" type="text" placeholder="Type to query" 2 autocomplete="off" spellcheck="off" 3 data-source="search"/>
1BigCollection = new Meteor.Collection('bigcollection'); 2 3if (Meteor.isServer) { 4 Meteor.startup(function() { 5 if (!BigCollection.find().count()) { 6 // fill BigCollection 7 } 8 }); 9 10 Meteor.methods({ 11 search: function(query, options) { 12 options = options || {}; 13 14 // guard against client-side DOS: hard limit to 50 15 if (options.limit) { 16 options.limit = Math.min(50, Math.abs(options.limit)); 17 } else { 18 options.limit = 50; 19 } 20 21 // TODO fix regexp to support multiple tokens 22 var regex = new RegExp("^" + query); 23 return BigCollection.find({name: {$regex: regex}}, options).fetch(); 24 } 25 }); 26} else { 27 28 Template.demo.helpers({ 29 search = function(query, sync, callback) { 30 Meteor.call('search', query, {}, function(err, res) { 31 if (err) { 32 console.log(err); 33 return; 34 } 35 callback(res.map(function(v){ return {value: v.name}; })); 36 }); 37 } 38 }); 39}
Catching selected event with id
1Template.example.rendered = function() { 2 Meteor.typeahead.inject(); 3} 4 5Template.example.helpers({ 6 items: function() { 7 // data source function 8 // TODO fetch items from meteor collection 9 return someCollections.find().fetch().map(function(object){ return {id: object._id, value: object.value}; }); 10 }, 11 selected: function(event, suggestion, datasetName) { 12 // event - the jQuery event object 13 // suggestion - the suggestion object 14 // datasetName - the name of the dataset the suggestion belongs to 15 // TODO your event handler here 16 console.log(suggestion.id); 17 } 18});
Template:
1<template name="example"> 2 <input placeholder="Kies een plaats" autocomplete="off" spellcheck="off" 3 data-source="items" data-select="selected"/> 4</template>
Styling
By default, there is no style applied with this package. If you want the same styling as in the demo app, please do the following:
- add bootstrap:
meteor add twbs:bootstrap
- add the style.css file to your application