I followed this tutorial on meteor search-source and modified the example so it would fit my current needs.
This is my collections.js
which is located in my lib directory
Guides = new Mongo.Collection("guides");
I have following code in my client side controller.
var options = {
keepHistory: 1000 * 60 * 5,
localSearch: true
};
var fields = ['title'];
GuideSearch = new SearchSource('guides', fields, options);
Template.guide_list.helpers({
getGuides: function () {
return GuideSearch.getData({
transform: function (matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
}
});
},
isLoading: function () {
return GuideSearch.getStatus().loading;
}
});
Template.guide_list.events({
"keyup #title": _.throttle(function(e) {
var text = $(e.target).val().trim();
GuideSearch.search(text);
}, 200)
});
And this as my server side code
SearchSource.defineSource('guides', function(searchText, options) {
if(searchText) {
var regExp = buildRegExp(searchText);
var selector = {title: regExp}
return Guides.find(selector, options).fetch();
} else {
return Guides.find({}, options).fetch();
}
});
function buildRegExp(searchText) {
// this is a dumb implementation
var parts = searchText.trim().split(/[ \-\:]+/);
return new RegExp("(" + parts.join('|') + ")", "ig");
}
For some reason, I receive the following error message when typing something into my input field
Exception in delivering result of invoking 'search.source': Meteor.makeErrorType/errorClass@http://10.0.3.162:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:525:15
._livedata_result@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:4625:23
Connection/onMessage@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3365:7
._launchConnection/self.socket.onmessage/<@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:2734:11
_.forEach@http://10.0.3.162:3000/packages/underscore.js?46eaedbdeb6e71c82af1b16f51c7da4127d6f285:149:7
._launchConnection/self.socket.onmessage@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:2733:9
REventTarget.prototype.dispatchEvent@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:173:9
SockJS.prototype._dispatchMessage@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:1158:5
SockJS.prototype._didMessage@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:1216:13
SockJS.websocket/that.ws.onmessage@http://10.0.3.162:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:1363:9
This is my template code:
template(name="guide_list")
.format-properly
.container-fluid
.input-group#adv-search
.form-horizontal(role="form" method="POST" action="#")
.col-md-6
.form-group
label(for="contain") Guide title
input.form-control(type="text" id="title")
.col-md-6
.form-group
label(for="contain") Author
input.form-control(type="text" name="author")
.col-md-6
.form-group
label(for="hero") Select a hero
select.form-control(name="hero")
option(value="all" selected) All Heroes
option(value="Druid") Druid
option(value="Hunter") Hunter
option(value="Mage") Mage
option(value="Paladin") Paladin
option(value="Priest") Priest
option(value="Rogue") Rogue
option(value="Shaman") Shaman
option(value="Warlock") Warlock
option(value="Warrior") Warrior
.col-md-6
.form-group
label(for="filter") Filter by
select.form-control(name="filterBy")
option(value="0" selected) All guides
option(value="most_viewed") Most viewed
option(value="top_rated") Top rated
option(value="most_commented") Most commented
.container-fluid
.table-responsive
table.table.table-hover
thead
tr
th hero
th title
th author
th updated
th dust
th
span.glyphicon.glyphicon-eye-open
th
span.glyphicon.glyphicon-heart
th
span.glyphicon.glyphicon-comment
tbody
each guides
tr
td {{hero}}
td
a(href="/guide/{{formatId _id}}") {{title}}
td {{authorUsername}}
td {{moFormat modifiedAt 'YYYY-MM-DD'}}
td {{dust}}
td {{hitCount}}
td {{rating}}
td {{commentCount}}
tbody
each getGuides
tr
td {{hero}}
td
a(href="/guide/{{formatId _id}}") {{title}}
td {{authorUsername}}
td {{moFormat modifiedAt 'YYYY-MM-DD'}}
td {{dust}}
td {{hitCount}}
td {{rating}}
td {{commentCount}}
Any help or suggestions are highly appreciated!
Edit: I updated the search-source
package to 1.4.2
I believe that the issue is the lack of options
passed to the GuideSearch.search
method.
This leads to the search definition handler getting null
for options.
SearchSource.defineSource('guides', function(searchText, options) {
if(searchText) {
var regExp = buildRegExp(searchText);
var selector = {title: regExp}
return Guides.find(selector, options).fetch(); //illegal
} else {
return Guides.find({}, options).fetch();
}
});
This results in the collection's find()
method getting a null options
arguments, that, if supplied, must be an object (and not null
).
Therefore, either check for a null options
in the data source and pass an empty object (or nothing) to the find()
method, or pass an empty object to the GuideSearch.search()
method:
Template.guide_list.events({
"keyup #title": _.throttle(function(e) {
var text = $(e.target).val().trim();
GuideSearch.search(text, {}); // here
}, 200)
});
You should probably make sure that the options
are not null
in the server method as in @webdeb's answer in any case.
Additionally, at the moment some packages (check
and ejson
) have to be added as dependencies to your app as the meteorhacks:search-source
package uses them without declaring a dependency. This is due to change introduced in v1.2.0. (before then those symbols were automatically available to packages).
In order to get have all of the results available initially, you can trigger a search when the template is first created. Note that this may be fairly costly when you have a lot of data, so you should probably limit the results returned from the search definition handler on the server.
Template.guide_list.onCreated(function () {
GuideSearch.search('', {});
});
In order to display the title correctly in the search results, you could use Spacebars.SafeString()
so that blaze knows to render it as HTML.
Template.guide_list.helpers({
getGuides: function () {
return GuideSearch.getData({
transform: function (matchText, regExp) {
return Spacebars.SafeString(matchText.replace(regExp, "<b>$&</b>"))
}
});
},
formatId: function(id) {
return id;
},
moFormat: function(date, format) {
return moment(date).format(format);
},
isLoading: function () {
return GuideSearch.getStatus().loading;
}
});
Template.guide_list.events({
"keyup #title": _.throttle(function(e) {
var text = $(e.target).val().trim();
GuideSearch.search(text, {/* your options here */});
}, 200)
});
or, alternatively, use the triple brace notation:
a(href="/guide/{{formatId _id}}") {{{title}}}
Warning: be sure to sanitize your matchText
when doing this.
Publications should have nothing to do with the results, as the package uses its own collection.
I agree with the answer from @MasterAM, but to solve this problem just put this in your SearchSource
function:
SearchSource.defineSource('guides', function(searchText, options) {
options = options || {}; // to be sure, that options is at least an empty object
if(searchText) {
var regExp = buildRegExp(searchText);
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With