1

I am working on a small application that displays a "users" JSON in an HTML5 table. It uses Bootstrap 3 and AngularJS. I want to paginate this table.

I do not have an array to loop through, with ng-repeat. I have the number of pages.

var root = 'https://jsonplaceholder.typicode.com';

    // Create an Angular module named "usersApp"
    var app = angular.module("usersApp", []);

    // Create controller for the "usersApp" module
    app.controller("usersCtrl", ["$scope", "$http", "$filter", function($scope, $http, $filter) {
      var url = root + "/users";
      $scope.userList = [];
      $scope.search = "";
      $scope.filterList = function() {
        $scope.userList = $filter('filter')($scope.users, $scope.search);
        $scope.itemsCount = $scope.userList.length;
        $scope.pageMax = Math.ceil($scope.itemsCount / $scope.perPage);
      };
      $http.get(url)
        .then(function(data) {
          // Users arary
          $scope.users = data.data;
          $scope.filterList();
          // Order by function
          $scope.orderByMe = function(criteria) {
            $scope.myOrderBy = criteria;
          }

          // Paginate
          $scope.pageNum = 1;
          $scope.perPage = 3;
          $scope.startAt = 0;
          $scope.filterList();
          
          $scope.currentPage = function() {
            $scope.startAt = $scope.index * $scope.perPage;
          };

          $scope.prevPage = function() {
            if ($scope.pageNum > 1) {
              $scope.pageNum = $scope.pageNum - 1;
              $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
            }
          };

          $scope.nextPage = function() {
            if ($scope.pageNum < $scope.pageMax) {
              $scope.pageNum = $scope.pageNum + 1;
              $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
            }
          };
        });
    }]);
.table-container {
  margin: 10px 0 0 0 !important;
}
.table-responsive {
  margin: 0 !important;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>


<div data-ng-app="usersApp">
  <div class="container" data-ng-controller="usersCtrl">
    <div class="panel panel-default table-container">
      <div class="panel-heading">Users</div>
      <div class="panel-body">
        <div class="row">
          <div class="col-sm-12">
            <div class="form-group search-box">
              <input type="text" class="form-control" id="search"
                     placeholder="Search User" data-ng-model="search"
                     ng-change="filterList()">
            </div>
          </div>
          <div class="col-sm-12">
            <div class="table-responsive">
              <table class="table table-striped table-bordered" id="dataTable">
                <thead>
                  <tr>
                    <th>#</th>
                    <th ng-click="orderByMe('name')">Full name</th>
                    <th ng-click="orderByMe('email')">Email</th>
                    <th ng-click="orderByMe('city')">City</th>
                    <th>Street</th>
                    <th>Suite</th>
                    <th>Zipcode</th>
                  </tr>
                </thead>
                <tbody>
                  <tr data-ng-repeat="user in userList|orderBy:myOrderBy| limitTo : perPage : startAt">
                    <td>{{$index + startAt + 1}}</td>
                    <td>{{user.name}}</td>
                    <td><a href="mailto:{{user.email  | lowercase}}">{{user.email  | lowercase}}</a></td>
                    <td>{{user.address.city}}</td>
                    <td>{{user.address.street}}</td>
                    <td>{{user.address.suite}}</td>
                    <td>{{user.address.zipcode}}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="text-center" ng-if="pageMax > 1">
      <ul class="pagination pagination-sm">
        <li><a href="#" ng-click="prevPage()"><i class="fa fa-chevron-left"></i></a></li>
        <li ng-repeat="n in [].constructor(pageMax) track by $index">
          <a href="#" ng-click="currentPage()">{{$index+1}}</a>
        </li>
        <li><a href="#" ng-click="nextPage()"><i class="fa fa-chevron-right"></i></a></li>
      </ul>
    </div>
  </div>
</div>

Whenever I click the Next and Previous page pagination items (the chevrons), the script works right, but when I click the numbered pagination items, the $scope.startAt variable does not update and the table row numbers are NaN.

What am I doing wrong?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252
  • Your code needs to be re-factored. It shouldn't be assigning functions to scope variables from inside a `.then` block. – georgeawg Jul 16 '18 at 21:08

1 Answers1

1

Invoke the currentPage function with $index as an argument:

  <ul class="pagination pagination-sm">
    <li><a href="#" ng-click="prevPage()"><i class="fa fa-chevron-left"></i></a></li>
    <li ng-repeat="n in [].constructor(pageMax) track by $index">
      ̶<̶a̶ ̶h̶r̶e̶f̶=̶"̶#̶"̶ ̶n̶g̶-̶c̶l̶i̶c̶k̶=̶"̶c̶u̶r̶r̶e̶n̶t̶P̶a̶g̶e̶(̶)̶"̶>̶{̶{̶$̶i̶n̶d̶e̶x̶+̶1̶}̶}̶<̶/̶a̶>̶
      <a href="#" ng-click="currentPage($index)">{{$index+1}}</a>
    </li>
    <li><a href="#" ng-click="nextPage()"><i class="fa fa-chevron-right"></i></a></li>
  </ul>

BEFORE

 $scope.currentPage = function() {
    $scope.startAt = $scope.index * $scope.perPage;
 };

After

  $scope.currentPage = function(index) {
      $scope.pageNum = index+1;
      $scope.startAt = index * $scope.perPage;
  };

How would you update the pagination with the search filter?

  $scope.filterList = function() {
    var oldList = $scope.userList || [];
    $scope.userList = $filter('filter')($scope.users, $scope.search);
    if (oldList.length != $scope.userList.length) {
        $scope.pageNum = 1;
        $scope.startAt = 0;
    };
    $scope.itemsCount = $scope.userList.length;
    $scope.pageMax = Math.ceil($scope.itemsCount / $scope.perPage);
  };

The DEMO

angular.module("usersApp", [])
.controller("usersCtrl", function($scope, $http, $filter) {
  var root = '//jsonplaceholder.typicode.com';
  var url = root + "/users";
  $scope.userList = [];
  $scope.search = "";

  $scope.filterList = function() {
    var oldList = $scope.userList || [];
    $scope.userList = $filter('filter')($scope.users, $scope.search);
    if (oldList.length != $scope.userList.length) {
        $scope.pageNum = 1;
        $scope.startAt = 0;
    };
    $scope.itemsCount = $scope.userList.length;
    $scope.pageMax = Math.ceil($scope.itemsCount / $scope.perPage);
  };

  // Order by function
  $scope.orderByMe = function(criteria) {
    $scope.myOrderBy = criteria;
  };

  $http.get(url)
    .then(function(response) {
      $scope.users = response.data;
      $scope.filterList();
      // Paginate
      $scope.pageNum = 1;
      $scope.perPage = 3;
      $scope.startAt = 0;
      $scope.filterList();
  });

  $scope.currentPage = function(index) {
    $scope.pageNum  = index+1;
    $scope.startAt = index * $scope.perPage;
  };

  $scope.prevPage = function() {
    if ($scope.pageNum > 1) {
      $scope.pageNum = $scope.pageNum - 1;
      $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
    }
  };
  $scope.nextPage = function() {
    if ($scope.pageNum < $scope.pageMax) {
      $scope.pageNum = $scope.pageNum + 1;
      $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
    }
  };
});
.table-container {
  margin: 10px 0 0 0 !important;
}
.table-responsive {
  margin: 0 !important;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>


<div data-ng-app="usersApp">
  <div class="container" data-ng-controller="usersCtrl">
    <div class="panel panel-default table-container">
      <div class="panel-heading">Users</div>
      <div class="panel-body">
        <div class="row">
          <div class="col-sm-12">
            <div class="form-group search-box">
              <input type="text" class="form-control" id="search" placeholder="Search User" data-ng-model="search" ng-change="filterList()">
            </div>
          </div>
          <div class="col-sm-12">
            <div class="table-responsive">
              <table class="table table-striped table-bordered" id="dataTable">
                <thead>
                  <tr>
                    <th>#</th>
                    <th ng-click="orderByMe('name')">Full name</th>
                    <th ng-click="orderByMe('email')">Email</th>
                    <th ng-click="orderByMe('city')">City</th>
                    <th>Street</th>
                    <th>Suite</th>
                    <th>Zipcode</th>
                  </tr>
                </thead>
                <tbody>
                  <tr data-ng-repeat="user in userList|orderBy:myOrderBy| limitTo : perPage : startAt">
                    <td>{{$index + startAt + 1}}</td>
                    <td>{{user.name}}</td>
                    <td><a href="mailto:{{user.email  | lowercase}}">{{user.email  | lowercase}}</a></td>
                    <td>{{user.address.city}}</td>
                    <td>{{user.address.street}}</td>
                    <td>{{user.address.suite}}</td>
                    <td>{{user.address.zipcode}}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="text-center" ng-if="pageMax > 1">
      <ul class="pagination pagination-sm">
        <li><a href="#" ng-click="prevPage()"><i class="fa fa-chevron-left"></i></a></li>
        <li ng-repeat="n in [].constructor(pageMax) track by $index"><a href="#" ng-click="currentPage($index)">{{$index+1}}</a>
        </li>
        <li><a href="#" ng-click="nextPage()"><i class="fa fa-chevron-right"></i></a></li>
      </ul>
    </div>
  </div>
</div>
georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Great answer. How would you update the pagination with the search filter? – Razvan Zamfir Jul 16 '18 at 20:47
  • Reset the page number if the filter changes the length of the list. See update. – georgeawg Jul 16 '18 at 21:57
  • I have made the table more symmetrical and saved some vertical space. There is a need for your approval of the edit in order for the effect to take place. – Razvan Zamfir Jul 18 '18 at 08:56
  • You seem to be a very experienced front-end developer. Please have a look at the question **[How can I eliminate white screen “pause” between animations?](https://stackoverflow.com/questions/51343571/jquery-eliminate-white-screen-pause-between-animations)**. Thanks! – Razvan Zamfir Jul 18 '18 at 15:57