Modularized approach for handling multiple filters on a Page


#1

I have a fairly complicated list which I need to filter (state, tags, name, categories) and sort. Some of the filter properties allow multi selection (eg: categories, tags) and some are single selection (eg: state). I have many more pages coming in pipeline which fit in the same category.

Now I have already build various components for filtering and sorting but facing issues in storing states of the various filter and sort selections.

Since filters generally fits into a category of key - value pair which can be applied on collection I want to isolate this logic from controller and put somewhere in a mixin / service.

For example whenever there is a change in selection in filter / sort I’m planning to send an action to parent controller from the component and update the state of the same (expecting this to happen automatically - may be by using naming convention or some other means) and components would be observing on some controller property and updates itself based on the change.

Now my question is how to implement this kind of feature in controller in Ember way so that I don’t have to write boilerplate in every controller.

Disclaimer : Ember newbie migrated from backbone - marionette


#2

I’m not sure if this is “the Ember way”, but we have this problem in our app too with a relatively similar complexity (user defined sort/filters). I think there are arguable a few different approaches here, so others may have a better way.

IMHO when you’re dealing with this complexity of a relationship between your sorting/filtering/searching/paging controls and a “data source” (controller/service) using {{actions}} and databindings becomes too cumbersome and even more prone to human error.


So we built a mixin that define the interface (java-like). Something like this:

  const BaseDataQueryMixin = Ember.Mixin.create({
    pageCount: Ember.computed(...), 

    applyFilter(/*filterValue*/) { },
    removeFilter(/*filterValue*/) { },

    filterList: Ember.computed(...),

    sortBy(/*filterSort*/) { },

    /* ... */
  });

Next we built a mixin or two that implements the reusable logic for this process.

  const DataQueryMixin = Ember.Mixin.create(BaseDataQueryMixin, {
    pageCount: Ember.compute.reads('serverMeta.totalPages'),
 
    applyFilter(filterValue) {
      this.get('_filters').addObject(filterValue);
      this.scheduleReloadData();
    }
  });

Then we used services (instead of controllers) to manage these. The advantage of services is two fold, 1) controllers will someday be deprecated and gone (that’s a ways off), succeed by services. 2) Services are more accessible for Components because you can use Ember.inject.service() to get access to it.

So with that in mind, it looked something like this:

  const PostService = Ember.Service.extend(DataQueryMixin, {
    /* ... */
    /* Implement / Override anything about DataQueryMixin that might be specific to blog posts. */
  });

Lastly, in our reusable sort/filter/search components we just had to provide them with the service and the relationship between the two was already defined by the interface.

  {{x-sort dataQueryService=postService}}
  {{x-filter dataQueryService=postService}}
  {{!-- .... --}}

This interface pattern isn’t often found in Ember. So someone else might have a completely different suggestion that is more of the Ember-Way. But sometimes it feels like swimming upstream to decouple things that have such an explicit relationship to each other.


#3

That’s an excellent solution. I have been breaking my head since 2 days.

I too was thinking on using the services but wasn’t sure how to.

Lets see what others think too.

Thank you.