backbone-paginator / backbone-pageable Goto Github PK
View Code? Open in Web Editor NEWDEPRECATED!!! backbone-pageable and backbone.paginator have merged. Please use backbone-paginator/backbone.paginator instead.
License: MIT License
DEPRECATED!!! backbone-pageable and backbone.paginator have merged. Please use backbone-paginator/backbone.paginator instead.
License: MIT License
I am preloading the collection with infinite mode.
Paging through works fine, but I have to manually add a new links entry for the 'next' page otherwise system returns false for hasNext() when it has reached the end of the collection.
E.g.
pageSize = 10
initial collection size = 20
links.length = 2
hasNext() = false when on page 2
Adding a links[3] = 'http...' after creating PageableCollection allows infinite to continue to work .. infinitely.
This ties in with #6 as well. Many public APIs that support pagination uses custom headers to deal with CORS and infinite paging, but since Backbone 0.9.9 has removed xhr
from parse
, it is no longer easy to just override parse
to process custom headers. Request headers could use some help too. Putting them in the fetch
methods every time a request needs to be made is cumbersome.
Some public APIs paginate with the Links header and don't return the total number of records, in that the paginator should support a mode that only pages backward and forward.
See:
This line in parseState
is overwriting the state with undefined if the server doesn't return all the values, which may result in weird states from time to time.
https://github.com/wyuenho/backbone-pageable/blob/1.1.7/lib/backbone-pageable.js#L1031
Since mosts states are recalculated on the client side anyway, this method should allow the server to return a partial state object so users don't have to explicitly override this method.
If backbone-pageable.js is loaded via AMD, then PageableCollection is not set on the Backbone object is not defined globally. However, Backbone.PageableCollection in backgrid.js:1526, in sort(). This causes an exception when the user attempts to sort (e.g. clicks a column header): 'Uncaught TypeError: Expecting a function in instanceof check, but got [object Object]'.
I was able to work around this issue by adding backgrid as a shim with an init method in my require.js config(). See below for that config.
Is this user error? Did I do it wrong? I think I'd expect Backgrid to specify PageableCollection as a dependency and reference that as a local. Thanks for the great library. :)
require.config({
baseUrl: '/js/app/',
paths: {
// Major libraries
underscore: '/js/vendor/underscore/underscore',
backbone: '/js/vendor/backbone/backbone',
mustache: '/js/vendor/mustache/mustache',
backgrid: '/js/vendor/backgrid/backgrid',
// Backbone plugins
'backbone-pageable': '/js/vendor/backbone-pageable/backbone-pageable',
},
shim: {
backgrid: {
exports: "Backgrid",
deps: ['underscore', 'backbone', 'backbone-pageable'],
init: function(_, Backbone, PageableCollection) {
this.Backbone.PageableCollection = PageableCollection;
}
}
}
});
Hey guys,
Probably a pretty easy one here, but my API stack (https://github.com/filtersquad/rocket_pants) with Kaminari (https://github.com/amatsuda/kaminari) returns results like the following:
{"response":[],"count":0,"pagination":{"current":1,"previous":null,"next":2,"per_page":30,"pages":0,"count":0}}
Most of which I've been able to translate very effectively in the settings block:
queryParams:
currentPage: "page"
pageSize: 10
totalPages: "pages"
totalRecords: "count"
Sadly Pageable gets upset that my total pages count is also zero-based (line 574). Is there a configuration fix here that I'm missing, or do we need do a PR? I hacked at it for a few minutes, but wasn't sure that bypassing the exception at like 574 would break the library elsewhere.
xhr.getResponseHeader('Link')
will return null if the response is a result of a CORS. Setting Access-Control-Expose-Headers to allow 'Link" has no effect.
Hi @wyuenho ,
First thanks for this great library. I'm trying to replace Backbone-paginator for a more stable library and found yours really good (and tested!).
I think I found a bug when adding/removing a model in the "client" mode. The status object is not consistent regarding the totalRecords and totalPages, and is not adjusted accordingly.
Additionally, when removing the last record of a page, the state continues to indicate that lastPage/currentPage is the empty page - I think behaviour should be that the empty page is automatically removed and the paginated collection points now to the lastPage-1 as the currentPage. The only exception should be when removing all the models from the collection - I think in this case I think totalPages and currentPage should be 1 (never 0 so not to break behaviour of the library).
I have committed to my fork tests for those issues (branch change_col_bug - alanrubin@f44b5fd). What do you think ?
I may have some time tomorrow to try to generate a fix for it.
Thanks,
Alan
There should be a private snapshot of initial states during initialization. states should be reset to the snapshot by default upon switching mode to avoid confusion, this behavior may be disabled with an option
Provide an option, method, or example for connecting API's that use URL paths rather than query strings.
Example:
http://some-api/posts/2/
http://some-api/sortedBy/alpha/page/2/perPage/10/
Backstory:
I've been finishing up some customizations to Backbone.Paginator, when I stumbled across your library. I really like it, though it's a bit rigid about the URL structure and response structure of the API you're connecting with. I think it can be improved by allowing more flexibility there.
Infinite paging often involves cases where previously fetched pages need to have their models retained for operations later on. Storing previously fetched pages in fullCollection
will satisfy this use case. So in essence, this mode will enable fetching page by page from the server, but storing the page on the client side once fetched. Paging backward will be done on the client side, but paging forward will use server-side mode.
This can be done in #initialize for added extra convenience.
When reseting the fullCollection with new models, the "state" (most specifically the totalRecords) are updated after the pageableCollection is reseted (backbone-pageable line 475-477).
In my case, the scenario is the following: If I have a pagination Backbone.View (renders the pagination controls for navigating - forward, back, pages number) listening to the "reset" event in the pageableCollection, it will be rendered with the previous state when a reset is done. That could be true for the event "remove" as well, but I still have to check that.
My tentative fix is the below code. I will add a test to demonstrate the issue.
if (event == "reset" || event == "sort") {
options = collection;
state.totalRecords = this.models.length;
pageableCollection.state = pageableCollection._checkState(state);
resetQuickly(pageableCollection, this.models.slice(pageStart, pageEnd),
options);
}
Tonight I looked closely with your source. I configed a parseRecords in my Collection. When finished fetching data, FullCollection gets to call the parseRecords which works fine. But then after models slice, when you trying to use the paged models to reset the PageCollection, the parseRecords called again with the plat data structure, which will cause the issue.
E.g.
Server Response
{ total_number:120, users:[0...120]}
parseRecords: (resp)->
resp.users
Fetching a collection throws error if the collection'l url is a method, not property.
Here is a jsfiddle to demonstrate the issue.
Can you add an example\sample application (like Netflix or localstorage one)?
It would be easier to understand and also play with some of the features you are claiming this library will solve better than 'backbone.paginator'?
Too many ways to sort a collection now. Most of them can be streamlined by making makeComparator
private and provide an alternative setSorting(sortKey, order, options)
, where options.side
can be "server"
or "client"
, and options.full
is a boolean value.
Provide an example for connecting API's that use complex response structures.
Example:
{
"totalPages": 100,
"page": 3,
"results": [
{"name": "jane", "age": 35},
{"name": "john", "age": 22},
]
}
The parse
method supplied modifies this.state
, but it's not immediately obvious how someone extending the class should use it.
This looks like it would be a proper method, but I'm not sure.
parse: function (resp) {
if (resp && resp.results) {
return new TypeError("The incoming data format did not match the format we expected.");
}
this.state = _checkState(_.defaults({
totalRecords: resp.totalPages * this.state.pageSize
}, this.state));
return resp.results;
}
Backstory:
I've been finishing up some customizations to Backbone.Paginator, when I stumbled across your library. I really like it, though it's a bit rigid about the URL structure and response structure of the API you're connecting with. I think it can be improved by allowing more flexibility there.
The links
hash doesn't exist before fetch so calling getFirstPage first will fail under infinite mode. This should not happen because it doesn't fail under the other modes.
I can't get this to work. Using it with backgrid, following your example code.
My PageableCollection.
Dummy parseLinks for testing.
var PageableActivityCollection = PageableCollection.extend(
{
model: ActivityModel,
mode: 'infinite',
state: {
pageSize: 20,
sortKey: 'created_at',
order: 1,
firstPage: 1,
currentPage: 1
},
initialize: function(options)
{
this.queryParams.link_id = options.link_id;
this.queryParams.link_type = options.link_type;
},
queryParams: {
totalPages: null,
pageSize: 'ps',
currentPage: 'page',
sortKey: "s",
order: "d",
directions: {
"-1": "asc",
"1": "desc"
}
},
url: '/data/activity',
parseLinks: function (resp, xhr)
{
return {
first: '/data/activity/1',
next: '/data/activity/2',
}
}
});
this.collection = new PageableActivityCollection(
{},
{
mode: 'infinite',
link_id: options.link_id,
link_type: options.link_type
});
this.collection.getFirstPage().complete(function ()
{
self.$el.find('.box-body .table-container').append(pageableGrid.render().$el);
self.$el.find('.box-body').append(footer.render().$el);
});
It fails on this line in getPage()
if (mode == "infinite") options.url = self.links[pageNum];
The issue is links has no value yet, no data has been retrieved from the server.
No matter what way I do this links is always undefined yet code references it, no fetch is ever performed.
What am I doing wrong?
The "Fix #1 playable demos" commit added JSON files with query parameters as part of the filename (territories.json?page=2&per_page=15
). While I'm able to clone the repo successfully on my macbook, it fails on my Windows 7 machine. I've tried using the command line and two different GUIs. All give me the same error message:
error: unable to create file examples/json/territories.json?page=1&per_page=15 (Invalid argument)
AFAIK, a ?
is an illegal character for a filename in Windows.
The rest of the files are present, but the repo is stuck in a dirty state due to the missing files. Normally not a big problem as it's just the sample data missing, but it does throw off the package manager I'm using (bower).
In server mode, could there be caching based on the queryParams? I.E. the same set of query params will only go to the server once.
I'm using pagination with a filter which means my API needs to return zero results sometimes. This causes the following Range Error to be thrown:
Uncaught RangeError: `currentPage` must be firstPage <= currentPage <= totalPages if 1-based. Got 1.
Where:
> firstPage
1
> currentPage
1
> totalPages
0
Even if I set totalPages to 1 in my API it gets set to zero which is causing the error to be thrown.
From #13 (comment)
a full collection with 9 totalRecords, and pageSize as 4. I call .getPage(3) at the backbone-pageable collection, going to page 3 (one element). I call .remove in the backbone-pageable collection (not the full collection) for the only element
RangeError: currentPage must be firstPage <= currentPage <= totalPages if 1-based
When adding models through collection.add([]) (passed as array) fullCollection is missing some models. If i add them one by one works like it's supposed to.
Using:
Example:
var TestModel = Backbone.Model.extend({})
var TestCollection = Backbone.PageableCollection.extend({
model: TestModel,
mode: "client",
state: {
firstPage: 0,
currentPage: 0,
pageSize: 3
},
comparator: function (model) {
return model.get("name");
}
})
var data = [
{
id: "1",
name: "test3"
},{
id: "2",
name: "test5"
},{
id: "3",
name: "test1"
},{
id: "4",
name: "test2"
},{
id: "5",
name: "test4"
},{
id: "6",
name: "test6"
},{
id: "7",
name: "test7"
},{
id: "8",
name: "test8"
}
];
var collection = new TestCollection();
collection.add(data);
console.log("data: ", _.pluck(data, "name"));
console.log("collection: ", collection.pluck("name"));
console.log("fullCollection: ", collection.fullCollection.pluck("name"));
collection.getNextPage();
console.log("collection (nextPage 1): ", collection.pluck("name"));
collection.getNextPage();
console.log("collection (nextPage 2): ", collection.pluck("name"));
Output
data: ["test3", "test5", "test1", "test2", "test4", "test6", "test7", "test8"]
collection: ["test1", "test2", "test3"]
fullCollection: ["test1", "test2", "test3", "test4", "test5"]
collection (nextPage 1): ["test4", "test5"]
Uncaught RangeError: `currentPage` must be firstPage <= currentPage < totalPages if 0-based. Got 2.
This is a regression from 0.9.9.
From #13 (comment)
condition (pageSize < 1 || pageSize > totalRecords) (_checkState line 510) will fail when the number of total records is less then the pageSize - scenario which I think is ok, as you can have a collection of 2 elements with a pageSize of 4.
So people can have access to state
inside their functions.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.