Skip to content

Instantly share code, notes, and snippets.

@jkresner
Last active December 12, 2015 04:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jkresner/4716553 to your computer and use it in GitHub Desktop.
Save jkresner/4716553 to your computer and use it in GitHub Desktop.
Filter + PagingCollection Improved. A Filtering collection is a collection that has 2 views of it's models. (1) The original set of models and a filtered view using some custom filtering logic defined in the child collection of the base FilteringCollection class. A PagingCollection inherits from FilteringCollection and can keep track of and retu…
""" FilteringCollection
(1) Holds an original AND a filtered view of it's models
(2) Can sort filtered models on any arbitrary attribute
- sort can be invoked multiple times with different attributes to get
sorted by A then B """
class FilteringCollection extends Backbone.Collection
# Use coffee's constructor function so we can enforce initialization code +
# let the child class use the standard Backbone initialize function to
# execute it's own initialization code
constructor: ->
# when the collection is reset, we need to set the filtered models
@on 'reset', @reset_filteredmodels, @
# Call Backbone.Collection ctor so backbone calls initialize function
Backbone.Collection::constructor.apply(@, arguments)
reset_filteredmodels: ->
@filteredmodels = @models
# We separate sorting from setting the filtered models & firing the sort event
# This give flexibility to use sort in combination with filter below without
# firing multiple events & causing views listening to both to render twice
sort_filteredmodels: (attr, direction, type) ->
@filteredmodels = @_sort attr, direction, type
@trigger 'sort'
# Implementation of bi-directional sort on any attribute of the models
_sort: (attr, direction, type) ->
if ! attr? then return @filteredmodels
if direction is 'up' || !direction? then @comparator = (m) -> m.get(attr)
else if type is 'String' then @comparator = (m) -> reversestring(m, attr)
else then @comparator = (m) -> -1 * m.get(attr)
_.sortBy @filteredmodels, @comparator
# Combine sort + filter & only trigger one event. We could still access _sort
# & _filter separately if we want ... (f) = some custom object
filter_filteredmodels: (f) ->
@filteredmodels = @_filter f
# Sort is an object like { attr: 'Name', direction: 'up', type: 'String' }
if f? && f.sort?
@filteredmodels = @_sort f.sort.attr, f.sort.direction, f.sort.type
@trigger 'filter'
_filter: (f) ->
console.log 'override _filter in child class'
@models # return non-implemented filter view of the models
""" PagingAndFilterCollection
(1) Can keep track of current page & return it using fetchpage() """
class PagingAndFilterCollection extends FilteringCollection
pagesize: 25 # can override in the child collection
# Again wire into coffee ctor to make sure we hook up events and still let
# child class use Backbone.Collection.initialize for it's own init code
constructor: (args) ->
# Called FilteringCollection ctor with calls Backbone.Collection ctor
FilteringCollection::constructor.apply(@, arguments)
# If we filter or sort the collection we go back to page one as the
# number of pages will most likely have changed
@on 'filter sort', @resetpaging, @
# Override base reset_filteredmodels and combine it with reset paging
reset_filteredmodels: ->
FilteringCollection::reset_filteredmodels.apply(@, arguments)
@resetpaging()
# Recalculate our total pages and reset our current page back to 1
resetpaging: ->
@totalpages = Math.ceil(@filteredmodels.length / @pagesize)
@currentpage = if @totalpages > 0 then 1 else 0
# Move to next, previous or a specific page number
setpage: (page) ->
if page is '»' && @currentpage < @totalpages then @setpage @currentpage+1
else if page is '«' && @currentpage > 1 then @setpage @currentpage-1
else
page = parseInt page
if page <= @totalpages && page > 0
@currentpage = page
@trigger('page')
# Return the models in the current page
fetchpage: ->
start = @pagesize * (@currentpage - 1)
_.first _.rest(@filteredmodels, start), @pagesize
# http://stackoverflow.com/questions/5636812/sorting-strings-in-reverse-order-with-backbone-js
reversestring = (m, attr) ->
if ! m.get(attr)? then return ''
String.fromCharCode.apply String, _.map(m.get(attr).split(""), (c) -> 0xffff - c.charCodeAt() )
class Tea extends Backbone.Model
class Teas extends FilteringAndPagingCollection
model: Tea
pagesize: 10
url: '/api/teas'
initialize: (args) ->
console.log 'can do initialize code here & base initialize code still gets called'
""" f might look like
{
origin: 'China',
type: ['Green','Oolong'],
search: 'Smokey',
sort: { attr: 'origin', direction: 'up', type: 'String' }
}
"""
_filter: (f) ->
r = @models
if f?
if @hasSelection f.origin
r = _.filter r, (m) => f.location is m.get('origin')
if @hasSelection f.type
r = _.filter r, (m) => _.contains f.type, m.get('type')
if @hasSelection f.search
pattern = new RegExp f.search, "gi"
r = _.filter r, (m) ->
pattern.test(m.get("name")) ||
pattern.test(m.get("description"))
r
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment