Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Expressive Backbone filters
# Expressive filters for Backbone Collections.
#
# (1) Define a filter:
#
# class MyCollectionFilter extends CollectionFilter
# matches: (model) ->
# model.get('title') == @query
#
# (2) Find matching models by applying it:
#
# sourceCollection = new MyCollection # add models
# target = new MyCollection
# filter = new MyCollectionFilter
# filter.from(sourceCollection).with('My Model Title').apply()
# models = filter.results
#
# Or bind it to another Collection that will receive the filtered results:
#
# target = new MyCollection
# filter = new MyCollectionFilter(target)
# filter.from(sourceCollection).with('My Model Title').apply()
# models = target.models
#
class CollectionFilter
constructor: (@_filteredCollection) ->
# Assigns the source collection. Pure sugar.
from: (@source) -> @
# Assigns a filter query. Pure sugar.
with: (@query) -> @
# Applies the filter
# Returns an array of models from the +@source+ collection that
# pass the gauntlet defined in +@matches+.
apply: ->
@preFilter() if @preFilter instanceof Function
@results = @source.filter @matches, @
# Update _filteredCollection, if one has been assigned
if @_filteredCollection
@applyTo @_filteredCollection
@postFilter() if @postFilter instanceof Function
@
applyTo: (collection) ->
# Pick the models that need to be added or removed
additions = _.difference(@results, collection.models)
removals = _.difference(collection.models, @results)
# Make it so.
collection.remove(removals)
collection.add(additions)
@
# Empty all results
# Pessimistic twin of +@reset()+
clear: ->
@query = ''
@results = []
@applyTo @_filteredCollection if @_filteredCollection
@
# Copy all results from source collection.
# Optimistic twin of +@clear()+
reset: ->
@query = ''
@results = @source.models
@applyTo @_filteredCollection if @_filteredCollection
@
# Model attributes to sift through
# Define +@searchAttributes+ in child filters
searchAttributes: []
# Matching function
# Child filters should overwrite +@matches+ to set up the filter's
# matching behavior.
#
# Default provides a simple test that the query exactly matches
# one of the attributes enumerated in +@searchAttributes+
matches: (model) ->
for attribute in @searchAttributes
attribute = model.get(attribute)
return true if attribute == @query
false
# Apply the filter to a collection
# The collection will be updated with any additional models matching
# the current filter, while those that no longer match will be
# removed
bindTo: (collection) ->
@_filteredCollection = collection
@
# Export
window.CollectionFilter = CollectionFilter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.