queryParams and toggling multiple values

I am using queryParams on an app to filter search results by category. I am storing all categories in a single queryParam called filter, separated by |. (e.g. /app/search?filter=cat1|cat2|cat3). I’m not using the array queryParam form because it makes the URL “scary” to users.

I want to configure the app such that clicking on category #1 with the example url I just mentioned sends the user to /app/search?filter=cat2|cat3. Clicking it again would send the user to /app/search?filter=cat1|cat2|cat3.

So far I have implemented this using an action on my ApplicationRoute:

actions: {
  toggle: function(slug) {
    var filters = this.controllerFor('categories').toggle(slug);
    this.transitionTo('services', {
      queryParams: {
        filter: filters.length > 0 ? filters.join('|') : null
      }
    });
  }
}

Here is the CategoryController:

Ember.ArrayController.extend({
  filtered: [],
  toggle: function(value) {
    var array = this.get('filtered');
    if (!array.contains(value)) {
      array.pushObject(value);
    } else {
      array.removeObject(value);
    }
    return array;
  },
  selected: function(key, value) {
    // setter
    if (arguments.length > 1) {
      // if the value is an array, we are receiving slugs from a
      // full page load and we can use it directly.
      if (!Ember.isArray(value)) {
        // otherwise calculate what would happen if we toggled the
        // requested filtering item on or off.
        value = this.toggle(this.get('filtered'), value);
      } else {
        // save array of filters
        this.set('filtered', value);
      }
    }
    // getter
    return this.get('filtered');
  }.property('filtered')
});

Here is the ServicesRoute:

Ember.Route.extend({
  queryParams: {
    filter: {
      refreshModel: true
    }
  },
  model: function(params) {
    // there has to be a better way to do this
    if (!params.filter) {
      return [];
    } else {
      return this.store.find('category', {
        slug: params.filter.split('|')
      }).then(function(models) {
        return models.map(function(model) {
          return model.get('slug');
        });
      });
    }
  },
  setupController: function(controller, slug) {
    this.controllerFor('categories').set('selected', slug);
  }
});

… and here is what a category link looks like:

<li {{action 'toggle' category.slug}}>{{category.name}}</li>

I’d like to do this with link-to so there is a proper anchor link for this item. It seems like it should be trivial to calculate a new queryParam based on what the filters would be if the current slug was added or removed, but it definitely isn’t. I’ve looked into extending Ember.LinkView, creating a custom helper, etc.

At the moment, I don’t see a viable way to do this. Is there one I’m not aware of?

3 Likes