Learning through seeing code I personally find to be most rewarding, so here is a simple demonstration of a nice search pattern. This is a pretty common type of autocomplete style search you have no doubt seen before (say atmosphere for example).
There's a couple of useful things shown here:
- We're defining our search query in a file that's shared between client & server. This means that our search cursor is not repeated in our publication and on the client! You'll notice also that we define it on the collection, this is purely aesthetic.
- Escaping the regular expression is important.
1. Escape util
common/utils.js
RegExp.escape = function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
2. Our search cursor
common/collections/books.js
Books = new Mongo.Collection('books');
Books.search = function(query) {
return Books.find({
name: { $regex: RegExp.escape(query), $options: 'i' }
}, {
limit: 20
});
};
3. Publication
server/publications.js
Meteor.publish('booksSearch', function(query) {
check(query, String);
if (_.isEmpty(query))
return this.ready();
return Books.search(query);
});
4. Template
client/views/books.html
<template name="books">
<input type="text" placeholder="Search..." value="{{booksSearchQuery}}">
{{#if searchResults.count}}
<ul>
{{#each searchResults}}
<li>{{name}}</li>
{{/each}}
</ul>
{{/if}}
</template>
5. Template handling
client/views/books.js
Tracker.autorun(function() {
if (Session.get('booksSearchQuery'))
Meteor.subscribe('booksSearch', Session.get('booksSearchQuery'));
});
Template.books.events({
'keyup [type=text]': function(event, template) {
Session.set('booksSearchQuery', event.target.value);
}
});
Template.books.helpers({
searchResults: function() {
return Books.search(Session.get('booksSearchQuery'));
},
booksSearchQuery: function() {
return Session.get('booksSearchQuery');
}
});
Roundup
This could be further improved in many ways! A couple of ideas might be implementing a loading template which can be displayed by watching the ready() method on our subscription handle. Also we might wish to _.debounce our keypress event so that it's not fired too often.