Last active
September 22, 2016 20:31
-
-
Save emileber/7c3b472fa45d8a208d9a8317bb2f38ae to your computer and use it in GitHub Desktop.
Backbone infinite scroll collection inspired by Backbone.paginator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var InfiniteCollection = Backbone.Collection.extend({ | |
state: { | |
pageSize: 5, // default value for a page | |
}, | |
queryParams: { | |
pageSize: "limit", | |
pagesLeft: null, | |
total: null, | |
sortKey: null, | |
order: null, | |
skip: function() { | |
return this.length; | |
} | |
}, | |
/** | |
* Default to client side sorting. | |
*/ | |
constructor: function(models, options) { | |
this.fullCollection = this; // FIXME | |
_.defaults(this.queryParams, InfiniteCollection.prototype.queryParams); | |
_.defaults(this.state, InfiniteCollection.prototype.state); | |
_.extend(this.state, { total: 0 }); | |
return InfiniteCollection.__super__.constructor.call(this, models, options); | |
}, | |
getFirstPage: function(opt) { | |
opt = _.extend({ fetch: true, reset: true }, opt); | |
if (opt.reset) this.reset(null, _.extend({ silent: true }, opt)); | |
return this.fetch(opt); | |
}, | |
getNextPage: function(opt) { | |
return this.fetch(_.extend({ | |
add: true, | |
remove: false, | |
merge: true | |
}, opt)); | |
}, | |
parse: function(resp) { | |
_.extend(this.state, this.parseState(resp)); | |
return resp.collection; | |
}, | |
parseState: function(resp) { | |
var left = resp.total - resp.collection.length - this.length; | |
return { | |
total: resp.total, | |
pagesLeft: Math.ceil(left / this.state.pageSize) | |
}; | |
}, | |
hasLeft: function() { | |
return this.state.total - this.length > 0; | |
}, | |
getTotal: function() { | |
return this.hasLeft() ? this.state.total : this.length; | |
}, | |
/** | |
* Map the query params to the data object used by the underlying ajax lib | |
* to construct the query string. | |
* @param {Object} data to extend | |
* @return {Object} of all the data to be inserted as a querystring. | |
*/ | |
getQueryStringData: function(data) { | |
data = data || {}; | |
var state = this.state; | |
_.each(this.queryParams, function(param, queryParamKey) { | |
if (_.isString(param)) { | |
var key = queryParamKey; | |
queryParamKey = param; | |
param = state[key]; | |
} else if (_.isFunction(param)) param = param.call(this); | |
if (param != null && _.isUndefined(data[queryParamKey])) { | |
data[queryParamKey] = param; | |
} | |
}, this); | |
return data; | |
}, | |
fetch: function(options) { | |
options = options || {}; | |
var data = options.data || {}, | |
url = options.url || this.url || "", | |
state = this.state; | |
if (_.isFunction(url)) url = url.call(this); | |
// dedup query params | |
var queryStringIndex = url.indexOf('?'); | |
if (queryStringIndex !== -1) { | |
_.extend(data, queryStringToParams(url.slice(queryStringIndex + 1))); | |
url = url.slice(0, queryStringIndex); | |
} | |
options.url = url; | |
// map the query params to the data object used by the underlying ajax lib | |
// to construct the query string | |
options.data = this.getQueryStringData(data); | |
return InfiniteCollection.__super__.fetch.call(this, options); | |
}, | |
}); | |
/** | |
* https://github.com/backbone-paginator/backbone.paginator/blob/93294f4865055245e5207008c6bd35be661c5c69/lib/backbone.paginator.js#L79 | |
*/ | |
function queryStringToParams(qs) { | |
var keyValue, key, value, ls, params = {}, | |
decode = decodeURIComponent; | |
var keyValueArray = qs.split('&'); | |
for (var i = 0, length = keyValueArray.length; i < length; i++) { | |
var param = keyValueArray[i]; | |
keyValue = param.split('='); | |
value = keyValue[1]; | |
if (value == null) value = true; | |
key = decode(keyValue[0]); | |
value = decode(value); | |
ls = params[key]; | |
if (_.isArray(ls)) ls.push(value); | |
else if (ls) params[key] = [ls, value]; | |
else params[key] = value; | |
} | |
return params; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var collection = new Notes(); | |
// react to new chunks of model being fetched from the server. | |
this.listenTo(collection, { | |
"update": onUpdate | |
}); | |
function onUpdate(collection, options) { | |
var added = options.changes.added; | |
if (added && added.length > 0) { | |
// render `added` model array | |
} | |
} | |
// automatically fetch with url: api/notes/?limit=5&skip=0 | |
collection.getFirstPage(); | |
// automatically fetch with url: api/notes/?limit=5&skip=5 | |
collection.getNextPage({ | |
context: this, | |
success: function() { /* same as other backbone function */ }, | |
}).always(callback); | |
function callback(){ | |
// do something after the new models were fetched. | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Example of how to use the infinite collection. | |
// | |
var Notes = InfiniteCollection.extend({ | |
model: Backbone.Model, | |
state: { | |
pageSize: 5, | |
sortKey: 'date', | |
order: 1, | |
}, | |
// sort by date as UTC string (reverse chronological order) | |
comparator: function(a, b) { | |
return -a.get('date').localeCompare(b.get('date')); | |
}, | |
url: "api/notes", | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment