Last active
February 6, 2016 12:44
-
-
Save domq/9491e02768e81fd0cb7d to your computer and use it in GitHub Desktop.
Search-as-you-type for Meteor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Search-as-you-type support. | |
* | |
* Inspired from meteorhacks:search-source, but uses a subscription instead of | |
* a call. Streams results. Signals end of search with success or error. | |
* Throttles searches to match the server-side speed. | |
*/ | |
Search = function Search(name) { | |
this.name = name; | |
this._resultsChannelName = name + ".results"; | |
if (Meteor.isClient) { | |
this.status = new ReactiveVar({status: "nosearchyet"}); | |
this.results = new Mongo.Collection(this._resultsChannelName); | |
} | |
}; | |
if (Meteor.isServer) { | |
_.extend(Search.prototype, { | |
publish: function publish(callback) { | |
var searchSelf = this; | |
Meteor.publish(searchSelf.name, function (query) { | |
var self = this; | |
var thisForCallback = { | |
/** | |
* Callback hands out a search result | |
* | |
* @param id | |
* @param document | |
*/ | |
added: _.bind(self.added, self, searchSelf._resultsChannelName), | |
/** | |
* Callback says there will be no more search results. | |
* | |
* @param opt_error | |
*/ | |
stop: function(opt_error) { | |
// .stop() normally empties the collection, which is undesired | |
// on the client side (that would blank the search results): | |
var wasSending = self._session._isSending; | |
try { | |
self._session._isSending = false; | |
self.stop(opt_error); | |
} finally { | |
self._session._isSending = wasSending; | |
} | |
} | |
}; | |
return callback.call(thisForCallback, query); | |
}); | |
} | |
}); | |
} | |
if (Meteor.isClient) { | |
_.extend(Search.prototype, { | |
search: function search(query) { | |
var self = this; | |
if (! self.status.get()) { | |
// Search already in progress; buffer it (one slot in buffer) | |
self._queued = query; | |
return; | |
} | |
self.status.set(undefined); | |
// Purge any previous search results now, but *don't* refresh | |
// the view. The first message (result or "nosub") trickling | |
// back will take care of it. | |
self.results._collection.remove({}, function (err, result) { | |
if (err) { | |
self.status.set(err); | |
} else { | |
Meteor.subscribe(self.name, query, { | |
onStop: function (maybeError) { | |
if (maybeError) { | |
self.status.set(maybeError); | |
} else { | |
self.status.set({status: "OK"}); | |
if (self._queued !== undefined) { | |
var queued = self._queued; | |
self._queued = undefined; | |
self.search(queued); | |
} | |
} | |
}}); | |
} | |
}); | |
}, | |
isLoading: function isLoading() { | |
return ! this.status.get(); | |
} | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment