Filtering Collection Data (Blaze) - What am I doing wrong?

The code below only loads data where task has the entry ‘Test’.

Meteor.publish 'activities', ->
Activities.find(task: 'Test')

How can I do the same when a button is clicked when the code is:

Meteor.publish 'activities', ->
Activities.find()

Here’s my code so far:

Client:

Template.dashboard.events({
'click .ButtonDemo': function (e) {
  e.preventDefault();
  console.log("Successful click");
  activities: Activities.find({task: 'Test'}).fetch()
}
});

Router:

@route "dashboard",
path: "/dashboard"
waitOn: ->
  [
    subs.subscribe 'activities'
  ]
data: ->
  activities: Activities.find({},{sort: {createdAt: -1}}).fetch()

You should define activities inside helpers object for dashboard template and not in events handlers level.
By the way, using flow-router with Template level subscription is more flexible.

Hi,

Thank you for the response. I’ve attempted to use helpers instead of events, still it’s not working however. I’m really new to this, coding not just Meteor, sorry if I’m missing something here. Would it be possible for you to show me? I’ve done the following:

Template.dashboard.helpers({
'click .ButtonDemo': function (e) {
e.preventDefault();
console.log("Successful click");
activities: Activities.find({task: 'Test'}).fetch()
}
});

Replace in your button click event: activities: Activities.find({task: 'Test'}).fetch()

With:

console.log(Activities.find({task: 'Test'}).fetch()); // Check if you found your respective result

Hi, thanks, I have done so, see my code below:

Template.dashboard.events({
'click .ButtonDemo': function (e) {
  e.preventDefault();
  console.log("Successful click");
  console.log(Activities.find({task: 'Test'}).fetch());
}
});

In the console I see the following, my displayed data remains the same however in the UI.

[Document]
0: Document {_id: "b8A5dRKstC6eDPfE9", task: "Test", createdAt: Sat Dec 28 2019 20:22:07 GMT+0000 (Greenwich Mean Time), owner: "hyqsfvn5zAZM42CNF", updatedAt: Mon Dec 30 2019 13:25:38 GMT+0000 (Greenwich Mean Time), …}
length: 1
__proto__: Array(0)

Could you advise further please?

What are you trying to do with that document? Console log is just to show you how to filter and retrieve the document :slight_smile:

Couple things to note. By doing:

Meteor.publish 'activities', ->
Activities.find(task: 'Test')

This will limit your subscription to only those that met that filter. So if you’re trying to filter for something else (like task: 'Foo') it won’t work because your publication is already only returning a filtered set of documents. This why when you console logged Activities.find({task: 'Test'}).fetch() you only saw documents that had task: 'Test'.

Also, you can’t bind a click event inside a Helper. So you’ll need to separate out what the helper does vs the click event.

Here’s one way to do what you’re after:

Meteor.publish('activities', function(keyword) { // allow keyword to be passed in
    check(keyword, String); // make sure you're using the check package to help with security/sanitization of client-inputted data. 
    if(keyword){
        return Activities.find({ task: keyword }); // if keyword, return the filtered set
    } else {
        return Activities.find({}); // maybe by default you're just returning everything. up to you. 
    }
});

Then in your template:

Template.dashboard.onCreated(function() {
    Session.set('filtered-task', null); // use a Session var
    this.subscribe('activities');
});

In your helper:

Template.dashboard.helpers({
    someHelper: function(){ // not sure how you named your helper, but it needs a name that you'll then use in your Blaze template
        var data = Activities.find({ task: Session.get('filtered-task') }); 
        // by default, the Session variable is "null", so this won't filter anything
        if(data.count()){
            return data;
        }
    }
});

Then in your event:

Template.dashboard.events({
    'click .ButtonDemo': function (e) {
          e.preventDefault();
          console.log("Successful click");
          Session.set('filtered-task', 'Test'); // or you could get the "Test" string from a dropdown or checkbox and use that rather than hard-coding it. but for testing purposes this should do the trick. 
    }
});

To recap, the Publication I defined just returns everything without filter. In the Template, you’ll subscribe to “activities”, and then in the helper you’ll return the document set filtered by Session.get('filtered-task') (which is null to start with). Then in your event, you can change the value of the Session variable, which will dynamically trigger the helper to return the newly filtered data.

Fwiw: use of the Session variables this way can be somewhat controversial in the Meteor community, as some denounce it while others find it works great. From my point of view, in 4+ years of working with Meteor I’ve found Session vars to work great, hence the proposed usage here.

2 Likes

Thank you for this, I’ve been digesting your arguments over the past few days. As well as accomplishing my task, I also understand how I’ve been going wrong so thank you again for the time taken in your above post explaining. It is very much appreciated! :slight_smile: