133
votes

I'm trying to add pages to my list. I followed the AngularJS tutorial, the one about smartphones and I'm trying to display only certain number of objects. Here is my html file:

  <div class='container-fluid'>
    <div class='row-fluid'>
        <div class='span2'>
            Search: <input ng-model='searchBar'>
            Sort by: 
            <select ng-model='orderProp'>
                <option value='name'>Alphabetical</option>
                <option value='age'>Newest</option>
            </select>
            You selected the phones to be ordered by: {{orderProp}}
        </div>

        <div class='span10'>
          <select ng-model='limit'>
            <option value='5'>Show 5 per page</option>
            <option value='10'>Show 10 per page</option>
            <option value='15'>Show 15 per page</option>
            <option value='20'>Show 20 per page</option>
          </select>
          <ul class='phones'>
            <li class='thumbnail' ng-repeat='phone in phones | filter:searchBar | orderBy:orderProp | limitTo:limit'>
                <a href='#/phones/{{phone.id}}' class='thumb'><img ng-src='{{phone.imageUrl}}'></a>
                <a href='#/phones/{{phone.id}}'>{{phone.name}}</a>
                <p>{{phone.snippet}}</p>
            </li>
          </ul>
        </div>
    </div>
  </div>

I've added a select tag with some values in order to limit the number of items that will be displayed. What I want now is to add the pagination to display the next 5, 10, etc.

I have a controller that works with this:

function PhoneListCtrl($scope, Phone){
    $scope.phones = Phone.query();
    $scope.orderProp = 'age';
    $scope.limit = 5;
}

And also I have a module in order to retrieve the data from the json files.

angular.module('phonecatServices', ['ngResource']).
    factory('Phone', function($resource){
        return $resource('phones/:phoneId.json', {}, {
            query: {method: 'GET', params:{phoneId:'phones'}, isArray:true}
        });
    });
6
When you say you want to implement next page and previous page, do you want the pagination to happen purely on client side or on server side. If the number of records are too high then you should opt for server side pagination. Under any scenario you need to start maintaining "startIndex" - limit would only provide number of records on page, apart from this you need to some how maintain current page - this can be done by maintaining startIndex.Rutesh Makhijani
I don't have a high number of records. What I wanted to do is to use the controller I already have (PhoneListCtrl). I don't know if it is server or client side. Sorry!Tomarto
@RuteshMakhijani I have a similar requirement with high number of records, please explain the reason behind using server side pagination for high number of recordsDinesh P.R.

6 Answers

216
votes

If you have not too much data, you can definitely do pagination by just storing all the data in the browser and filtering what's visible at a certain time.

Here's a simple pagination example: http://jsfiddle.net/2ZzZB/56/

That example was on the list of fiddles on the angular.js github wiki, which should be helpful: https://github.com/angular/angular.js/wiki/JsFiddle-Examples

EDIT: http://jsfiddle.net/2ZzZB/16/ to http://jsfiddle.net/2ZzZB/56/ (won't show "1/4.5" if there is 45 results)

39
votes

I just made a JSFiddle that show pagination + search + order by on each column using Build with Twitter Bootstrap code: http://jsfiddle.net/SAWsA/11/

15
votes

I've built a module that makes in-memory pagination incredibly simple.

It allows you to paginate by simply replacing ng-repeat with dir-paginate, specifying the items per page as a piped filter, and then dropping the controls wherever you like in the form of a single directive, <dir-pagination-controls>

To take the original example asked by Tomarto, it would look like this:

<ul class='phones'>
    <li class='thumbnail' dir-paginate='phone in phones | filter:searchBar | orderBy:orderProp | limitTo:limit | itemsPerPage: limit'>
            <a href='#/phones/{{phone.id}}' class='thumb'><img ng-src='{{phone.imageUrl}}'></a>
            <a href='#/phones/{{phone.id}}'>{{phone.name}}</a>
            <p>{{phone.snippet}}</p>
    </li>
</ul>

<dir-pagination-controls></dir-pagination-controls>

There is no need for any special pagination code in your controller. It's all handled internally by the module.

Demo: http://plnkr.co/edit/Wtkv71LIqUR4OhzhgpqL?p=preview

Source: dirPagination of GitHub

5
votes

I know this thread is old now but I am answering it to keep things a bit updated.

With Angular 1.4 and above you can directly use limitTo filter which apart from accepting the limit parameter also accepts a begin parameter.

Usage: {{ limitTo_expression | limitTo : limit : begin}}

So now you may not need to use any third party library to achieve something like pagination. I have created a fiddle to illustrate the same.

3
votes

Check out this directive: https://github.com/samu/angular-table

It automates sorting and pagination a lot and gives you enough freedom to customize your table/list however you want.

2
votes

Here is a demo code where there is pagination + Filtering with AngularJS :

https://codepen.io/lamjaguar/pen/yOrVym

JS :

var app=angular.module('myApp', []);

// alternate - https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
// alternate - http://fdietz.github.io/recipes-with-angular-js/common-user-interface-patterns/paginating-through-client-side-data.html

app.controller('MyCtrl', ['$scope', '$filter', function ($scope, $filter) {
    $scope.currentPage = 0;
    $scope.pageSize = 10;
    $scope.data = [];
    $scope.q = '';

    $scope.getData = function () {
      // needed for the pagination calc
      // https://docs.angularjs.org/api/ng/filter/filter
      return $filter('filter')($scope.data, $scope.q)
     /* 
       // manual filter
       // if u used this, remove the filter from html, remove above line and replace data with getData()

        var arr = [];
        if($scope.q == '') {
            arr = $scope.data;
        } else {
            for(var ea in $scope.data) {
                if($scope.data[ea].indexOf($scope.q) > -1) {
                    arr.push( $scope.data[ea] );
                }
            }
        }
        return arr;
       */
    }

    $scope.numberOfPages=function(){
        return Math.ceil($scope.getData().length/$scope.pageSize);                
    }

    for (var i=0; i<65; i++) {
        $scope.data.push("Item "+i);
    }
  // A watch to bring us back to the 
  // first pagination after each 
  // filtering
$scope.$watch('q', function(newValue,oldValue){             if(oldValue!=newValue){
      $scope.currentPage = 0;
  }
},true);
}]);

//We already have a limitTo filter built-in to angular,
//let's make a startFrom filter
app.filter('startFrom', function() {
    return function(input, start) {
        start = +start; //parse to int
        return input.slice(start);
    }
});

HTML :

<div ng-app="myApp" ng-controller="MyCtrl">
  <input ng-model="q" id="search" class="form-control" placeholder="Filter text">
  <select ng-model="pageSize" id="pageSize" class="form-control">
        <option value="5">5</option>
        <option value="10">10</option>
        <option value="15">15</option>
        <option value="20">20</option>
     </select>
  <ul>
    <li ng-repeat="item in data | filter:q | startFrom:currentPage*pageSize | limitTo:pageSize">
      {{item}}
    </li>
  </ul>
  <button ng-disabled="currentPage == 0" ng-click="currentPage=currentPage-1">
        Previous
    </button> {{currentPage+1}}/{{numberOfPages()}}
  <button ng-disabled="currentPage >= getData().length/pageSize - 1" ng-click="currentPage=currentPage+1">
        Next
    </button>
</div>