superRaytin / paginationjs

A jQuery plugin to provide simple yet fully customisable pagination.
http://pagination.js.org
MIT License
912 stars 659 forks source link

pageNumber have no effect #44

Closed dmitryrn closed 6 years ago

dmitryrn commented 7 years ago

I set pageNumber on init, but there is always 1

let pageNumberOnInit = 2

paginationContainer.pagination({
  dataSource: 'http://mytube.loc/api/posts',
  pageNumber: pageNumberOnInit,
  locator: 'posts',
  totalNumberLocator: (response) => {
    return response.count
  },
  pageSize: 5,
  className: 'paginationjs-theme-blue',
  ajax: {
    beforeSend: () => {
      $("html, body").animate({ scrollTop: 0 }, 600)
    }
  },
  callback: (response, pagination) => {
    this.pageNumber = pagination.pageNumber
    this.posts = response
  }
})
superRaytin commented 6 years ago

Default pageNumber will always be 1 if totalNumberLocator is set, see: https://github.com/superRaytin/paginationjs/blob/master/src/pagination.js#L785

This is because Pagination can not know exactly if page N exists.

dents commented 6 years ago

@fabiofdsantos Then what's the best way to have totalNumberLocator modify pageNumber after figuring out how many items are going to be shown?

fabiofdsantos commented 6 years ago

@dents, What's your scenario? Are you using plugin's ajax implementation?

Here's an example:

    $('#pagination').pagination({
        dataSource: HTTP_URL
        pageSize: 50,
        locator: 'results.items',
        totalNumberLocator: function(response) {
            return response.results.total;
        },
        alias: {
            pageNumber: 'page',
            pageSize: 'limit'
        },
        showPrevious: false,
        showNext: false,
        ajax: {
            beforeSend: function() {
                // add a spinner
            },
            complete: function(jqXHR, textStatus) {
                if (jqXHR.status === 200 || jqXHR.readyState == 0 || jqXHR.status == 0) {
                    return false; // do nothing
                } else if (jqXHR && jqXHR.status === 403) {
                    window.location.href = window.location.href.split('/').slice(0, 3).join('/') + '/login';
                } else {
                    alert('error');
                }
            },
        },
        callback: function(data, pagination) {
            console.log(data);
        }
    });
dents commented 6 years ago

@fabiofdsantos As of the latest version it's ajax: function() { return {beforeSend:...} } but yeah that is what I am using. The problem is when trying to show a specific page on first load (for example page is specified in a URL in a bookmark), there is no obvious way to do that without caling $('#pagination').pagination(properPageNum) in callback() and thereby incurring another trip to server.

fabiofdsantos commented 6 years ago

@dents, A potential workaround is to perform asynchronous HTTP requests outside the plugin's scope. Then, use the following code sample to manipulate attributes dataSource, pageSize and pageNumber:

    $('#demo').pagination({
        dataSource: [1, 2, 3, 4, 5, 6, 7, ... , 35],
        pageSize: 5,
        pageNumber: 3,
        callback: function(data, pagination) {
            // template method of yourself
            var html = template(data);
            dataContainer.html(html);
        }
    })
dents commented 6 years ago

@fabiofdsantos That approach would mean putting the paging logic outside then. It would work fine for the first page loading correctly but ajax would not be able to handle page changes, right? Because dataSource would be the static data for initial page.

fabiofdsantos commented 6 years ago

@dents, Yes, it seems that approach only works for a static dataSource. As @superRaytin said previously, currently the plugin can not know exactly if page X exists.

The default page number ("1") will be considered as long as totalNumberLocator is defined and we are using the async mode: https://github.com/superRaytin/paginationjs/blob/master/src/pagination.js#L75

dents commented 6 years ago

For anyone reading this later, I made peace with having two trips to server and modified beforeSend of ajax to tell server to only return totals on the first trip, and then actual page data on subsequent trips. Not ideal but works for now.

gituser0424 commented 5 years ago

@dents Could you please show your code about this?

I made peace with having two trips to server and modified beforeSend of ajax to tell server to only return totals on the first trip

I am not sure how to set totalNumber in beforeSend.

Arctic7 commented 4 years ago

@dents I think what you want is a regular server side render page at first load, and then using ajax to load other pages. This is very seo friendly. If you want to do that ,I have a solution.

$(function () {
    //when you first load this page use server side pagination params
    let isFirst = true;
    let totalCount = parseInt('<%=totalCount%>');
    $('.selector').pagination({
        dataSource:'/api/data',
        locator: 'data',
        pageSize:5,
        ajax:{
            beforeSend:function(xhr,data){
                if(isFirst){
                    //use a empty api to cheat pagination plugin, just return {data:[],totalCount:1}
                    data.url = '/api/pagination';
                }
            }
        },
        //since you already have the correct total number,just modify it here after a successful empty response
        totalNumberLocator: function(resp){
            if(isFirst){
                resp = {
                    data:[],
                    totalCount:totalCount
                };
            }
            return resp.totalCount;
        },
        callback:function(data,pagination){
            if(isFirst){
                //then finally erase the first load flag,so the rest page click will always be normal ajax request
                isFirst = false;
            }else{
                $('.content').html(data);
            }
        }
    });
});