Skip to content

Instantly share code, notes, and snippets.

@jamesarosen
Last active May 8, 2018 20:37
Show Gist options
  • Save jamesarosen/04294177307b9d47cd02 to your computer and use it in GitHub Desktop.
Save jamesarosen/04294177307b9d47cd02 to your computer and use it in GitHub Desktop.
Filtering Based On Multiple Fields in Ember

The Ember app I'm working on right now has a number of lists of elements. Some of those lists can be quite long. We could add pagination, but we decided that filtering would be a better solution.

I took a look at ember-list-filter. This library works and it has the basic features I'm looking for -- arbitrary template for the list item and arbitrary fields on which to filter. But its API isn't very modern-Ember.

In particular, I feel the iteration-item template should be a block and the fields should be positional (unnamed) parameters. So I whipped up a little component.

Usage

{{#filtered-list "title" "keywords" "author.name" content=model.posts placeholder="Search Posts" as |post|}}
  <span class='post__title'>{{post.title}}</span>
  <span class='post__author'>{{post.author.name}}</span>
{{/filtered-list}}

Everything within this post is released under the Apache License v2, copyright James A Rosen 2015.

import Ember from "ember";
// A utility function for filtering `items` by whether they have a field
// that matches `query`, case-insensitively. If `items` or `fields` is blank,
// returns an empty Array. If `query` is blank, returns all items.
export default function filterByFields(items, fields, query) {
items = items || [];
fields = fields || [];
query = (query || '').toLowerCase();
if (items.length === 0 || fields.length === 0) { return Ember.A(); }
if (query.length === 0) { return Ember.A(items); }
const results = items.filter(function(item) {
return fields.some(function(field) {
const target = Ember.getWithDefault(item, field, '').toLowerCase();
return target.indexOf(query) > -1;
});
});
return Ember.A(results);
}
import Ember from "ember";
import filterByFields from "../utils/filter-by-fields";
const { Component, computed } = Ember;
export default Component.extend({
tagName: 'section',
classNames: [ 'filtered-list' ],
positionalParams: 'filterFields',
placeholder: null,
filter: '',
content: null,
didUpdateAttrs() {
this.set('content', Ember.A(this.get("content")));
},
filterResults: computed('filter', 'filterFields.@each', 'content.@each', function() {
return filterByFields(this.get('content'), this.get('filterFields'), this.get('filter'));
}),
actions: {
filterChanged(e) {
this.set('filter', this.$(e.target).val());
}
}
});
<input value='' oninput={{action "filterChanged"}} placeholder="{{placeholder}}" class='filtered-list__input'>
<div class='filtered-list__results'>
{{#each filterResults as |result|}}
<div class='filtered-list__result'>{{yield result}}</div>
{{/each}}
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment