Meteor Pagination Issue

2019-01-20 18:14发布

问题:

I am having issues with pagination I've implemented. Pagination works based on server-sided publish skip & limit filter.

Issue #1. If I perform a specific user search the very first page will be blank.

At that state skip is set to 0, limit is always 20.

If I perform a find().fetch() I get 20 elements, but they are all for a different user.

Issue #2 Me going to next page (skip+10) gives me a few more elements

Doing it again gives even more results

and finally being out of data, and going to next page just removes 10 results, leaving 10 shown

This is a very odd behavior. Server-sided publish

Meteor.publish('overtime', function(opt){
if (!Roles.userIsInRole(this.userId, 'admin')) {
return Overtime.find({userId: this.userId}, opt);
} else {
return Overtime.find({}, opt);
}
});

Client-sided subscription

var defaultSkipStep = 10;
var defaultLimit = 20;
Template.triphtml.onCreated(function(){
 var instance = this;
 Session.set('limit',defaultLimit);
 instance.autorun(function(){


instance.subscribe('overtime', {skip:(Session.get('overSkip')||0), limit:(Session.get('limit')||defaultLimit), sort: {createdAt: -1}});
instance.subscribe('trips', {skip:(Session.get('tripSkip')||0), limit:(Session.get('limit')||defaultLimit), sort: {createdAt: -1}});
});

Next page click event

"click .nxtpage_over": function(event, template){
Session.set('overSkip', (Session.get('overSkip') || 0) + defaultSkipStep);
Session.set('limit', 20);
},

Submit event https://pastebin.com/btYCSQBD

Query that user sees main.js (client)

https://pastebin.com/tWakPDT1

main.html https://pastebin.com/4uMVFsNG

Any idea how to make it so that when I perform search for a specific user I get all 20 results just for that user, and next page gives me NEXT 20 elements, not showing any one the 20 I've just seen.

回答1:

Use below code for pagination. It is very ideal code, easy to understand and implement.

SERVER PUBLISH:

        Meteor.publish('Students', function (str, toSkip, tillLimit) {
        var regex = new RegExp(str, 'i');
        var data = Students.find( {
                                $or:[
                                    {"_id": {'$regex' : regex}},
                                    {"username": {'$regex' : regex}}
                                ]
                            },
                    {sort: {'time': -1}, skip: toSkip, limit : tillLimit});
      return data;
    });

    Meteor.publish('StudentsTotalCount', function (str) {
        var regex = new RegExp(str, 'i');
        Counts.publish(this, "StudentsTotalCount", Students.find({
                                $or:[
                                    {"_id": {'$regex' : regex}},
                                    {"username": {'$regex' : regex}}
                                ]
                            });
          );
    });

str is global search text from client side. Now, Since the user will click on next and previous button frequently. On the client side the Subscription must be reactive. For that you can you below code.

CLIENT SUBSCRIBE:

Inside some App_ShowStudents.js file, you can create subscription as below;

    Template.App_ShowStudents.onCreated(function(){
      this.userId = new ReactiveVar("");
      this.filterCriteria = new ReactiveVar("");
      //PAGINATION
      this.enablePrevious = new ReactiveVar(false);
      this.enableNext = new ReactiveVar(true);
      this.skip = new ReactiveVar(0);
      this.diff = new ReactiveVar(0);
      this.total = new ReactiveVar(0);
      this.limit = new ReactiveVar(10);

      this.autorun(() => {
        Meteor.subscribe('Students', this.filterCriteria.get(), this.skip.get(), this.limit.get());
        Meteor.subscribe('StudentsTotalCount', this.filterCriteria.get(), this.role.get());
      });
    }); 

HELPERS:

    Template.App_ShowStudents.helpers({
        students(){
            return Students.findOne({}); // this will always be less or equal to 10.
        },
          skip(){
            return Template.instance().skip.get();
          },
          diff(){
            var instance = Template.instance();
            var add = instance.skip.get() + instance.limit.get();
            var total = instance.total.get();
            var diff = (add >= total ? total : add);
            instance.diff.set(diff);
            return instance.diff.get();
          },
          total(){
            Template.instance().total.set(Counts.get('StudentsTotalCount'));
            return Template.instance().total.get();
          }
    });

total actually must be reactive and should give only count as per your search criteria, so we had a seperate publish on server.

EVENTS:

    Template.App_ShowStudents.events({
        'click .previous': function (event, template) {
             event.preventDefault();
             if(template.diff.get() > template.limit.get()){
               template.skip.set(template.skip.get() - template.limit.get());
             }
          },
        'click .next': function (event, template) {
         event.preventDefault();
         if(template.diff.get() < template.total.get()){
           template.skip.set(template.skip.get() + template.limit.get());
         }
        },
        'input #searchData': function( event, template ) {
            if ( $( event.target ).val()) { // has some value.
              template.filterCriteria.set( $( event.target ).val() );
            } else {
              template.filterCriteria.set( "" );
            }
            console.log(template.filterCriteria.get());
          },
    });

App_ShowStudents.html

    <template name="App_ShowStudents">
       {{#if Template.subscriptionsReady}}
            <body>
                <input type="text" id="searchData" class="form-control input-lg" placeholder="Search ID, Username" />
                Showing {{skip}} - {{diff}} of {{total}}
                {{#each students}}
                    ...
                {{/each}}
                <ul>
                  <li><a href="#" role="button" class="previous">Previous</a></li>
                  <li><a href="#" role="button" class="next">Next</a></li>
                </ul>
            </body>
        {{else}}
            Please wait...
        {{/if}}
    </template>