Heroku: http://ng-crossfilter.herokuapp.com/
Angular uses native JavaScript methods for sorting, whereas ngCrossfilter
uses Crossfilter for a significant improvement in performance.
Firstly you need to initialise Crossfilter with your collection of items.
$scope.$ngc = new Crossfilter(response.data, 'id', 'persistent', ['name', 'age']);
ngCrossfilter
's constructor accepts four parameters – with only the first being mandatory. With the second parameter you can change the primary key – which will otherwise default to the first property in the first model; the third parameter allows you to change the filtering strategy – either persistent
or transient
, whereas the fourth parameter allows you to specify which properties to create dimensions with.
Once you've configured your Crossfilter collection, you can begin filtering and sorting. From within your view you should reference your collection – in our case, $ngc
.
<button ng-click="$ngc.filterBy('word', word)">Filter</button>
After you've applied all of your filters, you simply need to iterate over the array using the ng-repeat
directive.
<li ng-repeat="model in $ngc.collection() | limitTo: 100">
Filter by word:
$ngc.filterBy('word', word)
Third argument allows the specifying of a custom filtering function – see custom functions.
Unfilter by word:
$ngc.unfilterBy('word')
Unfilter all:
$ngc.unfilterAll()
$scope.fuzzyFilter = function fuzzyFilter(expected, actual) {
var regExp = new RegExp(expected);
return actual.match(regExp, 'i');
}
Which can then be utilised by passing it as the third argument in your view.
<button ng-click="$ngc.filterBy('word', word, fuzzyFilter)">
Sort by word:
$ngc.sortBy('word')
Second argument allows you to choose whether the sorting is by ascending – by not applying a value, the ascending will be inverted each time the same property is sorted on.
Unsort all:
$ngc.unsortAll()
First argument prevents the reverting of the sort order to ascending.
With filters it's useful to compute the length
of any given property and value pair – with ngCrossfilter
we can do this with the countBy
method.
$ngc.countBy('word', 'Adam')
However, there is one proviso and that is the countBy
method may not behave as you expect it to as it disregards the dimension you're counting on – see Crossfilter's Map-Reduce documentation.
In a nutshell, if you're filtering on the name
property, and you're also counting by the name
property, the name
filter will be disregarded – instead you need to count on a dimension that you're not using in the filtering process – unless the default behaviour is useful – and in most cases it actually makes sense – see the Word Count filter in the example. You can implement this by adding a custom dimension with addDimension
, or by counting on the primary key – assuming it's not being used in the filtering process.
Sometimes it is preferable to subscribe to the update of the Crossfilter, and to apply any modifications of your data at that point rather than relying on Angular's dirty-checking.
In these cases you can listen to the crossfilter/updated
event, which passes along the collection and the identifier of the Crossfilter.
$scope.$on('crossfilter/updated', function(event, collection, identifier) {
// Voila!
});
The identifier is empty by default, but can be set at any point.
$ngc.identifyAs('myCrossfilter');
It is possible to disable and re-enable the broadcasting of the crossfilter/updated
event. For example, the following would broadcast a single event.
$ngc.disableBroadcastEvent();
$ngc.sortBy('population');
$ngc.unfilterBy('city');
$ngc.enableBroadcastEvent();
$ngc.filterBy('climate', [5, 10]);
If you need full control over when the event is triggered, you may disable the event as described above and broadcast it yourself.
$ngc.broadcastEvent(true);
As there are common filtering techniques that Crossfilter doesn't implement, ngCrossfilter
comes with a fine selection of bundled filters for common tasks.
Fuzzy Filter
With the fuzzy filter you can filter with incomplete expressions.
$ngc.filterBy('city', 'M', $ngc.filters.fuzzy());
You will notice that the fuzzy
method is invoking the method immediately – this allows you to pass valid regular expression flags for insensitivity, et cetera...
$ngc.filterBy('city', 'M', $ngc.filters.fuzzy('i'));
By default no flags will be defined for the regular expression matching.
Regular Expression Filter
With the regular expression filtering you can specify an expression to filter on.
$ngc.filterBy('city', /o$/, $ngc.filters.regexp());
You can pass either an expression or an actual RegExp
object to the filter.
InArray Filter
With the inArray
filter you can check an array against an array – using the some
and every
array methods – please check the browser support before using it – although ngCrossfilter
will fallback to Underscore.js if it's installed.
$ngc.filterBy('twinCities', ['Beijing', 'Tokyo'], $ngc.filters.inArray());
By default the inArray
filter uses the every
method, which means in the above example only entries where twinCities
has both Beijing and Tokyo will be returned – you can use some
instead by passing it into inArray
filter method.
$ngc.filterBy('twinCities', ['Beijing', 'Tokyo'], $ngc.filters.inArray('some'));
To invert the inArray
filter, use the notInArray
filter with the same parameters.
DateTime Filter
Allows you to select a date/time range irrespective of the format of the time and/or date – Moment.js is a requirement to use this filter.
$ngc.filterBy('added', ['2012-01-01', '2012-12-01'],
$ngc.filters.dateTimeRange('YYYY-MM-DD'));
The first parameter of the dateTimeRange
filter allows you to specify the exact format – see Moment.js documentation – the default being YYYY-MM-DD. With the second parameter you can pass a comparator function for custom range filtering.
$ngc.filterBy('added', ['2012-01-01', '2012-12-01'],
$ngc.filters.dateTimeRange('YYYY-MM-DD'),
function(current, start, end) {
return (current > start);
});
dateTimeRange
also accepts -Infinity
/Infinity
ranges for where lows and highs are not applicable.
$ngc.filterBy('added', [-Infinity, '2012-12-01'],
$ngc.filters.dateTimeRange('YYYY-MM-DD'));
Bitwise Filter
Simple filter using the bitwise &
operator against the collection.
$ngc.filterBy('climate', 2, $ngc.filters.bitwise());
You can invert the filtering by passing an exclamation mark as the first argument to the bitwise
method.
$ngc.filterBy('climate', 2, $ngc.filters.bitwise('!'));
With Crossfilter updating a model requires removing it from the collection and then re-adding it. If you use this with one of your own primary keys then the PK will be added to the delete list, and therefore re-adding a model with the same PK will still be deleted. Therefore to make Crossfilter work with new models, you need to update the PK first – you can do this yourself, or you can use the convenient updateModel
that ngCrossfilter
provides – but you will be delegating the PK to ngCrossfilter
.
In order to use updateModel
you must define your primary key as $id
.
var $ngc = new Crossfilter(collection, '$id');
// ...
$ngc.updateModel(personModel, { name: 'Adam' });
With the $id
PK in place, the updateModel
will do all of the manual labour for you! You needn't worry about updating the PK yourself.
For the entire list of features for ngCrossfilter
it is advised to refer to the unit tests – as these have full coverage of all ngCrossfilter
methods and their usages.
Accessors
first
: First model in the collection;last
: Last model in the collection;models
: Retrieve a slice of the collection;crossfilter
: Retrieve an instance ofcrossfilter
;
Dimensions
addDimension
: Add a custom dimension;deleteDimension
: Delete a dimension;
Convenience
countBy
: Count values – see counting;groupBy
: Group by any given dimension;
Manipulation
addModel
: Add a model to the collection;addModels
: Add models to the collection;deleteModel
: Delete a model from the collection;deleteModels
: Delete models from the collection;restoreModel
: Restore a model from the garbage;restoreModels
: Restore models from the garbage;updateModel
: Update a model – see [update model](#update-model);
As with all of my projects, you're more than welcome to contribute. Please include a unit test for your additions, and if the Travis build passes then it will be merged into master.