Paginator module for AngularJS, in combination with Laravel's paginator class
/**
* Paginator
*
* The paginator can be used within any controller to easily set up
* some pagination options, including number of pages, page navigation
* and more. It is built to work with Laravel's own pagination library,
* which returns data in a particular format.
*
* Usage:
* Before you can use paginator, make sure you specify the URLs to your pagination
* templates, like so. In this example, I will assume that App is your angular application instance:
*
* App.value('paginator.config', {
* templates: {
* info: '/partials/pagination/info.html',
* pagination: '/partials/pagination/pagination.html'
* }
* });
*
* Example:
* Inside your controller, add the following. The first argument is your scope variable.
* Second argument is the name of the service/module you'll be using to fetch the data. Third
* is the name of the $scope.params.property that you want to store the data on, and 4th is the callback
* method, if needed, for doing data transformations.
*
* Paginate($scope, Category, 'categories');
*
* @param object $scope
* @param object Service - The Service to use. This could be the actual service (Such as User, in which
* case Paginator will assume the method "get"), or a custom method for the service,
* such as User.retrieve.
* @param string records - Name of the records variable to set the data to on $scope
* @param function callback - For when the paginator makes queries,
* can allow the developer to customize the data's format
* @author Kirk Bushell
* @date 9th January 2013
*/
var module = angular.module('core.paginator', []);
module.factory('Paginate', [function() {
return function($scope, Service, records, callback) {
if (!$scope.params) $scope.params = {};
// Defaults
$scope.currentPage = 0;
$scope.params.per_page = 10;
$scope.params.order = null;
var fieldOrder = 'asc';
function setPagination(data) {
$scope[records] = data;
$scope.params.per_page = $scope[records].per_page;
$scope.total = $scope[records].total;
$scope.totalPages = $scope[records].last;
$scope.pages = [];
for(var i=1; i<=$scope.totalPages;i++) {
$scope.pages.push(i);
}
$scope.nextPage = function() {
if($scope.currentPage < $scope.totalPages) {
$scope.currentPage++;
}
}
$scope.prevPage = function() {
if($scope.currentPage > 1) {
$scope.currentPage--;
}
}
$scope.firstPage = function() {
$scope.currentPage = 1;
}
$scope.lastPage = function() {
$scope.currentPage = $scope.totalPages;
}
$scope.setPage = function(page) {
$scope.currentPage = page;
$scope.params.page = page;
}
if (callback) callback($scope);
// Trigger an event that the pagination has been updated.
$scope.$broadcast('pagination.updated');
}
/**
* Trigger to update the pagination and the table data.
*
* The only optional param is which page to update to. If nothing
* is passed, it will return to page 1.
*/
$scope.$on('pagination.update', function( e , newPage ) {
if ( !newPage ) newPage = 1;
$scope.params.page = newPage;
for ( var i in $scope.params ) {
if ( $scope.params[i] == null || $scope.params[i] == 'null' ) {
delete $scope.params[i];
}
}
var caller = Service.get;
caller($scope.params, function(data) {
setPagination(data);
});
})
/**
* Trigger to reload data based on filters.
*
* If we're on the first page, we trigger the update function, but if
* we're on any other page, all we need to do is update the variable
* and that will automatically trigger the update.
*/
$scope.filter = function() {
if ( $scope.currentPage == 1 ) {
$scope.$broadcast('pagination.update', [ 1 ]);
}
else {
$scope.currentPage = 1;
}
};
/**
* Sets the ordering for the data to be retrieved from the server
*/
$scope.order = function(field) {
if ($scope.params.order == field) {
fieldOrder = fieldOrder == 'asc' ? 'desc' : 'asc';
}
$scope.params.direction = fieldOrder;
$scope.params.order = field;
// Trigger an update for the pagination.
$scope.$broadcast('pagination.update', [ 1 ]);
};
// Watch the currentPage variable and update the pagination whenever it changes.
$scope.$watch('currentPage', function( newPage , oldPage ) {
$scope.$broadcast('pagination.update', [ newPage ]);
});
// Execute first call
$scope.currentPage = 1;
}
}]);
/**
* Used for rendering the actual pagination.
*/
module.directive('pagination', ['paginator.config', function(config) {
return {
templateUrl: viewPath(config.templates.pagination)
};
}]);
/**
* Used for rendering the actual pagination.
*/
module.directive('paginationInfo', ['paginator.config', function(config) {
return {
templateUrl: viewPath(config.templates.info),
link: function(scope, iElement, iAttrs) {
scope.min = 0;
scope.max = 0;
// Listen to the event 'pagination.updated' and update the pagination
// info directive.
scope.$on('pagination.updated', function( e ) {
// Calculate the min/max by comparing the params.page variable, instead of the
// currentPage. The params.page variable is ALWAYS updated whereas currentPage will
// not be updated in cases where all items are deleted from page 2.
scope.min = scope.params.page * scope.params.per_page - scope.params.per_page + 1;
scope.max = scope.params.page * scope.params.per_page;
// Obviously if max is greater than total, we have very few records
// or we're at the end of the pagination.
if (scope.max > scope.total) scope.max = scope.total;
});
}
};
}]);
/**
* Used for setting sort options for a field
*/
module.directive('paginateSort', ['paginator.config', function(config) {
return {
link: function(scope, element, attrs) {
var field = attrs.paginateSort;
// Add the class 'sortable' to give a default styling.
element.addClass('sortable');
element.on('click', function() {
scope.order(field);
element.removeClass('sort-asc sort-desc').addClass( 'sort-' + scope.params.direction );
});
scope.$watch('params.order', function(newValue, oldValue) {
if (newValue && newValue != field)
element.removeClass('sort-asc sort-desc');
});
}
};
}]);