This package provides a simple mechanism to paginate your data subscriptions in Meteor.
NOTE: this is not blaze-compatible. Take a look at meteor-pages instead
- Install meteorite
mrt add paginator
###How To Use
Meteor Paginator provides templates and callbacks to easily page your subscribed data, provided you follow the below conventions.
####Server Code
You'll need to expose your publication with arguments for limit
and skip
Meteor.publish("myCollection", function(skip, limit) {
return MyCollection.find({}, {
skip: skip || 0
, limit: limit || 10
});
});
Next, you'll need a server method that gives you a total record count for your collection.
Meteor.methods({
totalCount: function() {
return MyCollection.find().count();
}
});
####Client Code
Wire up your subscription with Session
variables for both skip
and limit
Deps.autorun(function() {
Meteor.subHandle = Meteor.subscribe("myCollection", Session.get("pagingSkip"), Session.get("pagingLimit"));
}
Next, in your Template
where you want to page the data, instantiate the Paginator:
var _pager = new Meteor.Paginator({
templates: {
content: "my_template"
}
, pagination: {
resultsPerPage: 5 //default limit
}
, callbacks: {
onPagingCompleted: function(skip, limit) {
Session.set("pagingSkip", skip);
Session.set("pagingLimit", limit);
}
, getDependentSubscriptionsHandles: function() {
return [Meteor.subHandle];
}
, getTotalRecords: function(cb) {
//you need to return the total record count here
//using the provided callback
Meteor.call("totalCount", function(err, result) {
cb(result);
});
}
, onTemplateRendered: function() {
//regular render code
}
, onTemplateCreated: function() {
Session.set("pagingSkip", 0);
Session.set("pagingLimit", 5);
}
}
});
And ensure you're sending in the collection to the template:
Template.my_template.person = function() {
return MyCollection.find();
};
Finally, in your HTML
<table class="table table-striped table-bordered">
<tr>
<th>Name</th>
<th>Occupation</th>
</tr>
<tr>
{{#each person}}
<td>{{name}}</td>
<td>{{occupation}}</td>
{{/each}}
</tr>
</table>
<div class="well well-small">
{{> paginationButtons data }}
{{> selectPerPage data }}
</div>
Both {{{paginationPuttons}}}
and {{{selectPerPage}}}
are dynamically-creatd templates included in Meteor.Paginator. The styling all comes from Twitter Bootstrap.
Live Example: http://multipage.meteor.com
You may want to page the same data twice, or page different data, within the same view. The below example illustrates how this works.
Kittens = new Meteor.Collection("kittens");
KittensView = new Meteor.Collection("kittensView");
Puppies = new Meteor.Collection("puppies");
if (Meteor.isClient) {
Template.kittens_template.kitten = function() {
return Kittens.find();
};
Template.kittens_view_template.kitten = function() {
return KittensView.find();
};
Template.puppies_template.puppy = function() {
return Puppies.find();
};
function _pager(templ, num, handle) {
var p = new Meteor.Paginator({
templates: {
content: templ + "_template"
}
, pagination: {
resultsPerPage: 5 //default limit
}
, callbacks: {
onPagingCompleted: function(skip, limit) {
Session.set("pagingSkip_" + num, skip);
Session.set("pagingLimit_" + num, limit);
console.log("setting " + num);
}
, getDependentSubscriptionsHandles: function() {
return [handle];
}
, getTotalRecords: function(cb) {
//you need to return the total record count here
//using the provided callback
Meteor.call("totalCount", templ, function(err, result) {
cb(result);
});
}
, onTemplateRendered: function() {
//regular render code
}
, onTemplateCreated: function() {
Session.set("pagingSkip_" + num, 0);
Session.set("pagingLimit_" + num, 5);
}
}
});
return p;
}
Template.kittens_parent.created = function() {
Deps.autorun(function() {
Meteor.subHandle1 = Meteor.subscribe("kittens", Session.get("pagingSkip_1"), Session.get("pagingLimit_1"));
});
Meteor.Pager1 = _pager("kittens", 1, Meteor.subHandle1);
};
Template.puppies_parent.created = function() {
Deps.autorun(function() {
Meteor.subHandle2 = Meteor.subscribe("puppies", Session.get("pagingSkip_2"), Session.get("pagingLimit_2"));
});
Meteor.pager2 = _pager("puppies", 2, Meteor.subHandle2);
};
Template.kittens_view_parent.created = function() {
Deps.autorun(function() {
Meteor.subHandle3 = Meteor.subscribe("kittens2", Session.get("pagingSkip_3"), Session.get("pagingLimit_3"));
});
Meteor.pager3 = _pager("kittens_view", 3, Meteor.subHandle3);
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Kittens.find().count() === 0) {
_.times(500, function(n) {
Kittens.insert({ number: n, name: Random.hexString(5), toy: Random.hexString(10) })
});
}
if (Puppies.find().count() === 0) {
_.times(500, function(n) {
Puppies.insert({ number: n, name: Random.hexString(7), toy: Random.hexString(10) })
});
}
});
Meteor.publish("kittens", function(skip, limit) {
return Kittens.find({}, {
skip: skip || 0
, limit: limit || 10
});
});
Meteor.publish("kittens2", function(skip, limit) {
var self = this, skip = skip || 0, limit = limit || 10;
self.ready();
var _watchKittens = Kittens.find({}, {skip: skip, limit: limit}).observe({
added: function(doc) {
self.added("kittensView", doc._id, doc);
}
, changed: function(doc) {
console.log("changed ", init);
self.changed("kittensView", doc._id, doc);
}
, removed: function(doc) {
self.removed("kittensView", doc._id, doc);
}
});
self.onStop(function () {
_watchKittens.stop();
});
});
Meteor.publish("puppies", function(skip, limit) {
return Puppies.find({}, {
skip: skip || 0
, limit: limit || 10
});
});
Meteor.methods({
totalCount: function(templ) {
switch (templ) {
case "kittens":
case "kittens_view":
console.log("total", templ);
return Kittens.find().count();
case "puppies":
return Puppies.find().count();
}
}
});
}
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span6">
{{> kittens_parent}}
</div>
<div class="span6">
{{> puppies_parent}}
</div>
</div>
<div class="row-fluid">
<div class="span6">
{{> kittens_view_parent}}
</div>
</div>
</div>
</body>
<template name="kittens_parent">
{{> kittens_template}}
</template>
<template name="kittens_view_parent">
{{> kittens_view_template}}
</template>
<template name="puppies_parent">
{{> puppies_template }}
</template>
<template name="kittens_template">
<h2>Kittens</h2>
<table class="table table-striped table-bordered">
<tr>
<th>Number</th>
<th>Name</th>
<th>Toy</th>
</tr>
{{#each kitten}}
<tr>
<td>{{number}}</td>
<td>{{name}}</td>
<td>{{toy}}</td>
</tr>
{{/each}}
</table>
<div class="well well-small">
{{> paginationButtons data }}
{{> selectPerPage data }}
</div>
</template>
<template name="kittens_view_template">
<h2>Kittens View</h2>
<table class="table table-striped table-bordered">
<tr>
<th>Number</th>
<th>Name</th>
<th>Toy</th>
</tr>
{{#each kitten}}
<tr>
<td>{{number}}</td>
<td>{{name}}</td>
<td>{{toy}}</td>
</tr>
{{/each}}
</table>
<div class="well well-small">
{{> paginationButtons data }}
{{> selectPerPage data }}
</div>
</template>
<template name="puppies_template">
<h2>Puppies</h2>
<table class="table table-striped table-bordered">
<tr>
<th>Number</th>
<th>Name</th>
<th>Toy</th>
</tr>
{{#each puppy}}
<tr>
<td>{{number}}</td>
<td>{{name}}</td>
<td>{{toy}}</td>
</tr>
{{/each}}
</table>
<div class="well well-small">
{{> paginationButtons data }}
{{> selectPerPage data }}
</div>
</template>