Skip to content

Commit

Permalink
Re-initialize new repository
Browse files Browse the repository at this point in the history
  • Loading branch information
UsamaAshraf committed Oct 14, 2016
1 parent 335e27a commit 6d229b8
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 2 deletions.
174 changes: 172 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,172 @@
# basic-node-paginator
A basic, lightweight and independent paginator for the popular 'mysql' node module.
This is an easily integratable paginator for the popular [mysql](https://www.npmjs.com/package/mysql) node module.

The generated links are by default compatible with the [Bootstrap CSS framework](http://getbootstrap.com/).

The CSS classes applied to the links can be overridden.

Custom queries with joins, special clauses, aggregates etc can also be provided.


If your MySQL version or configuration causes problems with SQL_CALC_FOUND_ROWS / FOUND_ROWS(),
then please install version 1.2.5.

Otherwise, you can install version 1.2.2 and expect better performance.

## Table of Contents

- [Install](#install)
- [Setup](#setup)
- [State](#state)
- [Example](#example)


## Install

```sh
$ npm install basic-node-paginator
```

## Setup

Here is an example of a mysql configuration you may be using.
Make sure to export the get() method that returns the connection pool:
```js
// db.js

var mysql = require('mysql');
var PRODUCTION_DB = 'productiondb';
var TEST_DB = 'testdb'

exports.MODE_TEST = 'mode_test'
exports.MODE_PRODUCTION = 'mode_production'

var state = {
pool: null,
mode: null,
}

exports.connect = function (mode, done)
{
state.pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: '@#$1!%%',
database: mode === exports.MODE_PRODUCTION ? PRODUCTION_DB : TEST_DB
})

state.mode = mode
done()
}

exports.get = function ()
{
return state.pool
}
```

This is the paging module that will export a paginator object. The createPaginator() factory method
initializes the state of the paginator object it returns.

```js
// paging.js

var db = require('./db');
var paginator = require('basic-node-paginator');

var MyPaginator = paginator.createPaginator({
dbConn: db,
pageParam: '/'
});

exports.MyPaginator = MyPaginator;
```


The paginator object exposes a few methods:

* `configureInstance(obj)` : Meant to change the state of the paginator.

* `getPage(page_num, success, error)`


## State

The state of the paginator includes:

* `pageSize`: The number of records to be displayed per page. The default is 25.

* `tableName`: The table from where to retrieve the records.

* `customQuery`: A user-defined MySQL query that will be used instead of `table_name` if provided. Example:

`SELECT users.name, posts.text FROM users INNER JOIN posts on users.id = posts.user_id`

Note: You can use [Squel.js](https://hiddentao.github.io/squel/) to generate queries.

* `linksClasses`: The space-separated classes applied to the ul element of the links. The default is 'pagination'.

* `inactiveClasses`: The space-separated classes applied to the inactive pages' li elements within the list.

* `activeClasses`: The space-separated classes applied to the current page's li element within the list. The default is 'active'.

* `dbConn`: The config module which exports a get() method returning the mysql pool object.

* `baseUrl`: This is the base url that all the generated links will point to.

* `pageParam`: The name of the paramater that will be appended to the base url in the links.
This will contain the page number to be requested.
It can be set to '/' for cleaner page urls like:

```txt
/admin/users/1
```

as opposed to something like:

```txt
/admin/users/?page=1
```


## Example

This is what the route may look like:

```txt
/admin/users/:page?
```

Here is a method that may serve the request to this route.

```js
var paginator = require('./paging');

var page_num = req.params.page ? req.params.page : 1;

paginator.MyPaginator.configureInstance({
pageSize: 5,
tableName : 'users',
baseUrl: '/admin/users'
});

paginator.MyPaginator.getPage(page_num,
function (obj) {

return res.render('users', {users: obj.records, links: obj.links});

}, function (err) {

return res.render('error');
});
```

And in your users.jade file:

```jade
row.text-center !{links}
each user in users
<!-- Display users in a table or something. -->
```

Note: This can be easily done with some other templating engine as well. Jade is used only for demonstration.
30 changes: 30 additions & 0 deletions helpers/links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function getUrl(base, param)
{
if(param == '/') {

return base.slice(-1) == '/' ? base : base + '/';
}
else {
return base.slice(-1) == '/' ? base + '?' + param + '=' : base + '/?' + param + '=';
}
}


exports.getLinks = function (_pageNum, numOfPages, recordCount, baseUrl, pagingParam, linksClasses, activeClasses, inactiveClasses)
{
var links = "<ul class='" + linksClasses + "'>";
var url = getUrl(baseUrl, pagingParam);

for(var i = 1; i <= numOfPages; ++i) {

if(i == _pageNum) {
links += "<li class='"+activeClasses+"'><span>" + i + "</span></li>";
}
else {
links += "<li class='"+inactiveClasses+"'><a href='"+ url + i +"'>" + i + "</a></li>";
}
}

links += "</ul>";
return links;
}
29 changes: 29 additions & 0 deletions helpers/runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
exports.getTotalCount = function getTotalCount(_db_connection, _table_name, custom_query, done)
{
var query = custom_query ? "SELECT COUNT(*) as cn FROM (" + custom_query + ") _t_" :
"SELECT COUNT(*) as cn FROM " + _table_name;

_db_connection.get().query(query,
function (err, cnt) {
if (err) { return done(err, null); }
else {
return done(null, cnt[0].cn);
}
})
}


exports.getRecords = function getRecords(_pageNum, _db_connection, _table_name, _records_per_page, custom_query, callback)
{
var query = custom_query ? custom_query + " LIMIT ?, ?" : "SELECT * FROM " + _table_name + " LIMIT ?, ?";

_db_connection.get().query(query, [_records_per_page * (_pageNum-1), _records_per_page],
function (err, rows) {
if (err) {
return callback(err, null);
}
else {
return callback(null, rows);
}
})
}
10 changes: 10 additions & 0 deletions helpers/validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
exports.isAlphaOrParen = function (str) {

return /[a-z]/i.test(str) || str == '/';
}


exports.isPositiveInt = function(_num) {

return (parseInt(_num, 10) > 0);
}
84 changes: 84 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
var validator = require('./helpers/validators');
var links = require('./helpers/links');
var runner = require('./helpers/runner');


function Paginator(obj) {

if(! validator.isAlphaOrParen(obj.pageParam))
throw new Error("Invalid page parameter.");

if(! obj.hasOwnProperty('dbConn'))
throw new Error("Database connection not provided.");

this.db_connection = obj.dbConn;
this.table_name = obj.hasOwnProperty('tableName') ? obj.tableName : null;
this.records_per_page = obj.hasOwnProperty('pageSize') ? obj.pageSize : 25;
this.base_url = obj.hasOwnProperty('baseUrl') ? obj.baseUrl : null;
this.paging_param_name = obj.hasOwnProperty('pageParam') ? obj.pageParam : null;

this.link_ul_classes = obj.hasOwnProperty('linksClasses') ? obj.linksClasses : 'pagination';
this.link_inactive_li_classes = obj.hasOwnProperty('inactiveClasses') ? obj.inactiveClasses : '';
this.link_active_li_classes = obj.hasOwnProperty('activeClasses') ? obj.activeClasses : 'active';

this.custom_query = obj.customQuery ? obj.customQuery : null;
}


Paginator.prototype.configureInstance = function configureInstance(obj) {

if(obj.hasOwnProperty('pageSize') && ! validator.isPositiveInt(obj.pageSize))
throw new Error("Invalid page size.");

this.base_url = obj.hasOwnProperty('baseUrl') ? obj.baseUrl : this.base_url;
this.table_name = obj.hasOwnProperty('tableName') ? obj.tableName : this.table_name;
this.records_per_page = obj.hasOwnProperty('pageSize') ? obj.pageSize : this.records_per_page;
this.db_connection = obj.hasOwnProperty('dbConn') ? obj.dbConn : this.db_connection;
this.paging_param_name = obj.hasOwnProperty('pageParam') ? obj.pageParam : this.paging_param_name;

this.link_ul_classes = obj.hasOwnProperty('linksClasses') ? obj.linksClasses : 'pagination';
this.link_inactive_li_classes = obj.hasOwnProperty('inactiveClasses') ? obj.inactiveClasses : '';
this.link_active_li_classes = obj.hasOwnProperty('activeClasses') ? obj.activeClasses : 'active';

this.custom_query = obj.customQuery ? obj.customQuery : null;
};


Paginator.prototype.getPage = function getPage(_pageNum, success, error) {

if(! this.db_connection || (! this.table_name && ! this.custom_query) ||
! this.records_per_page || ! _pageNum || ! this.base_url || ! validator.isPositiveInt(_pageNum))
{
return error("Invalid configuration or parameters found.");
}

var self = this;

runner.getRecords(_pageNum, this.db_connection, this.table_name, this.records_per_page, this.custom_query,
function(err, result) {

if(err || result.length == 0) {
return error(err);
}

runner.getTotalCount(self.db_connection, self.table_name, self.custom_query,
function(_err, recordCount) {

if(_err || recordCount == 0) {
return error(err);
}

links_html =
links.getLinks(_pageNum, Math.ceil(recordCount / self.records_per_page), recordCount,
self.base_url, self.paging_param_name, self.link_ul_classes, self.link_active_li_classes, self.link_inactive_li_classes);

return success({records: result, links: links_html});
});
});
}


exports.createPaginator = function createPaginator(obj) {

return new Paginator(obj);
}
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "basic-node-paginator",
"version": "1.2.6",
"description": "A basic, lightweight and independent paginator for the popular 'mysql' node module.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/UsamaAshraf/basic-node-paginator.git"
},
"keywords": [
"basic",
"simple",
"node",
"paging",
"paginator"
],
"author": "Usama Ashraf <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/UsamaAshraf/basic-node-paginator/issues"
},
"homepage": "https://github.com/UsamaAshraf/basic-node-paginator#readme"

}

0 comments on commit 6d229b8

Please sign in to comment.