aliigleed
aliigleed

Reputation: 238

Multiple filters on a collection with angular-meteor

I'm following the Meteor To-Do App tutorial with Angular integration and am learning about filtering collections. I've been able to implement a simple filter on a collection for the app I'm working on by following the principles in the tutorial, but now I'm stuck trying to figure out how to add multiple queries to the filter.

In the example, you can view incomplete tasks by toggling a checkbox. This is implemented in the controller by watching $scope.hideCompleted for changes and passing it as a Mongo query to filter the Meteor collection.

Watcher

$scope.$watch('hideCompleted', function() {
  if ($scope.hideCompleted)
    $scope.query = {checked: {$ne: true}};
  else
    $scope.query = {};
});

Collection filter

$scope.tasks = $meteor.collection(function() {
  return Tasks.find($scope.getReactively('query'), {sort: {createdAt: -1}})
});

How do I make the query support multiple filters? For example, say I've chosen to extend the example and have ranked each to-do item by priority. I then would have an an input field for the user to filter the collection by priority, whose value is bound to $scope.priority. Now, if I wanted to filter the to-do list by incomplete and priority=$scope.priority tasks, I understand the Mongo query would need to be something along the lines of Tasks.find({ $and: [{ checked: {$ne: true} },{ priority: $scope.priority }]},{ sort: { createdAt: -1 } }).

In my app, I've been able to make two watchers properly track changes to two scope variables, analogous to my example with $scope.hideCompleted and $scope.priority, but I don't know how to take the next step to merge the queries when filtering the collection. I've also tinkered around a little with this package, since I'll eventually hope to be able to filter and sort by many criteria, but I didn't get too far with it before switching to the concepts I've described here.

I'd appreciate any help with this. Thank you!

Upvotes: 0

Views: 488

Answers (2)

Tj Gienger
Tj Gienger

Reputation: 1405

This would be my approach:

$meteor.autorun($scope, function() {
    // uncomment subscribe after you've got to that point
    // $scope.$meteorSubscribe('yourSubscription').then(function() {
        $scope.tasks = $scope.$meteorCollection(function() {
            return Tasks.find({ 
                checked: $scope.getReactively('model.hideCompleted'),
                priority: $scope.getReactively('model.priority')
            }, { sort: { createdAt: -1 } });
        });
    // });
});

A couple of things here:

  1. Once you have removed autopublish you can uncomment the $scope.$meteorSubscribe method and replace "yourSubscription" with the name of your actual subscription.
  2. $meteor.autorun will fire every time any getReactively variable changes.
  3. $scope.$meteorSubscribe and $scope.$meteorCollection are favored as they will remove the subscriptions and object/collection when the scope is destroyed.

If you have any issues then perhaps I can setup a demo for you to look at.

Upvotes: 1

aliigleed
aliigleed

Reputation: 238

Well, I guess I was a lot closer than I had expected, so I'll answer my question and share what I did to implement multiple filters with regards to the hypothetical extension of the to-do app.

I made hideCompleted and priority scope variables into properties of a scope object model, and used a single watcher with the argument true at the end to check for object equality (for any changes to model or its properties). I then generated $scope.query by stitching together "sub-queries." I've added the code below.

This seems to be working fine for now, but I'm not sure if it's the best solution, so I will continue experimenting, and I will update my answer if I find anything better. I'd be interested in any other approaches, though!

Watcher

var initQuery=true;    
var subQueries=[];
$scope.$watch('model', function() {
  if (!initQuery){
    subQueries=[];
    if ($scope.model.hideCompleted)
      subQueries.push({checked: {$ne: true}});
    if ($scope.model.priority)
      subQueries.push({priority: $scope.model.priority});
    $scope.query = { $and: subQueries};
  } else {
    initQuery = false;
    $scope.query = {};
  }
}, true);

Filter Collections (unchanged)

$scope.tasks = $meteor.collection(function() {
  return Tasks.find($scope.getReactively('query'), {sort: {createdAt: -1}})
});

Upvotes: 0

Related Questions