Skip to content

Instantly share code, notes, and snippets.

@webketje
Last active December 13, 2015 12:48
Show Gist options
  • Save webketje/3bd7f383f4ec074d1eb3 to your computer and use it in GitHub Desktop.
Save webketje/3bd7f383f4ec074d1eb3 to your computer and use it in GitHub Desktop.
Knockout cumulative array filters extender

ko.extenders.filters

Example usage

JS

var arr = ko.observableArray([
  {name: 'Kyle', age: 20, city: 'New York'},
  {name: 'Stan', age: 31, city: 'Amsterdam'},
  {name: 'Eric', age: 27, city: 'Paris'},
  {name: 'Butters', age: 24, city: 'New York'},
]).extend({
  filters: {
    olderThan25 : function(person) { return person.age > 25; }, // Stan
    inNewYork   : function(person) { return person.city === 'New York'; }, // Butters, Kyle
    name4Letters: function(person) { return person.name.length === 4; } // Eric, Kyle, Stan
  }
});

ko.applyBindings({persons: arr});

HTML

<nav data-bind="with: persons.filters">
  <label for="older-than-25">Older than 25</label>
  <input type="checkbox" name="older-than-25" data-bind="checked: olderThan25.active">
  <label for="in-new-york">In New York</label>
  <input type="checkbox" name="in-new-york" data-bind="checked: inNewYork.active">
  <label for="name-4-letters">Name with 4 letters</label>
  <input type="checkbox" name="name-4-letters" data-bind="checked: name4Letters.active">
</nav>
<ul data-bind="foreach: persons.filtered">
  <li data-bind="text: 'Name: ' + name + ', age: ' + age + ', city: ' + city"></li>
</ul>

Properties

Extending an observableArray with the filters extender creates the following properties on the observableArray:

  • observableArray.filtered : the resulting array from passing all active filters
  • observableArray.filters : an object containing all filters available to the observableArray. Each filter function can be accessed/ overwritten as observableArray.filters[filterName].filter, and its current state with observableArray.filters[filterName].active()

Methods

Extending an observableArray with the filters extender creates the following methods on the observableArray:

  • observableArray.toggleFilter(filterName[, force=null]) : Toggles filter filterName. If optional parameter force (true/false) is passed, sets the filter to the specified state.
  • observableArray.clearFilters() : Clears all filters.
  • observableArray.invert(): Inverts the filter selection.

Live Examples

<iframe width="100%" height="300" src="//jsfiddle.net/zeelux/B4vN2/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
/**
* @module ko.extenders.filters
* @desc Allows for cumulative filters on Knockout observable arrays
* @version 1
* @author Kevin Van Lierde (Tyblitz)
* @license MIT license, Copyright (c) 2015, Kevin Van Lierde (https://opensource.org/licenses/MIT)
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd)
define(['knockout'], factory);
else
factory(root.ko);
}(this, function(ko) {
'use strict';
ko.extenders.filters = function(target, filters) {
if (!/object|array/.test(typeof target()))
throw new TypeError('The \'filters\' extender only works with observable arrays');
var inverse = false,
filters = target.filters = ko.utils.objectMap(filters, function(filter, name) {
return {
filter : filter,
active: ko.observable(false)
};
}),
filterQueue = ko.pureComputed(function() {
var result = [];
ko.utils.objectForEach(filters, function(name, filter) {
if (filter.active())
result.push(name);
});
return result;
});
var passFilterTest = function(filterName, arr) {
return ko.utils.arrayFilter(arr, function(entry, i) {
return filters[filterName].filter(entry, i);
});
}, passFilterTestInverse = function(filterName, arr) {
return ko.utils.arrayFilter(arr, function(entry, i) {
return filters[filterName].filter(entry, i);
});
};
target.filtered = ko.pureComputed(function() {
var result = target(),
test = !inverse ? passFilterTest : passFilterTestInverse;
ko.utils.arrayForEach(filterQueue(), function(filterName) {
result = test(filterName, result);
});
return result;
}, target);
// toggles a filter
target.toggleFilter = function(filterName, force) {
filters[filterName].active(typeof force === 'boolean' ? force : !filters[filterName].active());
return target;
};
// clears all filters
target.clearFilters = function() {
ko.utils.objectForEach(filters, function(name, filter) {
filters[name].active(false);
});
return target;
};
// inverts all filters
target.invert = function() {
inverse = !inverse;
target.valueHasMutated();
return target;
};
return target;
};
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment