Search code examples
jqueryangularjsbindingangularjs-scope

AngularJS 1.6 pagination not displaying


I'm new with Angular, and I'm building off of a previous tutorial created by Jason Watmore (http://jasonwatmore.com/post/2016/01/31/angularjs-pagination-example-with-logic-like-google) on pagination.

I've gotten pretty close to making it work, but I'm obviously missing some critical step(s).

My sample app will go out, fetch the data, and return it, but it's not displaying in the div on the page, and the pagination isn't working (as expected).

Index.html

<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>

<body>
  <div class="contentWrapper">
     <div data-ng-controller="searchBtnCtrl as vm">
  <h3>Search:</h3>
  <input id="searchQuery" type="text" placeholder="enter your search query here" data-ng-model="searchInput" />

  <button id="searchBtn" data-ng-click="performSearch()">SEARCH</button>

  <div id="searchResults">
    <!-- items being paged -->
    <div ng-repeat="post in vm.posts"><a href='{{post.title}}' alt='{{post.title}}'>{{post.title}}</a><br />{{post.body}}</div>

    <!-- pager -->
    <ul ng-if="vm.pager.pages.length" class="pagination">
      <li ng-class="{disabled:vm.pager.currentPage === 1}">
        <a ng-click="vm.setPage(1)">First</a>
      </li>
      <li ng-class="{disabled:vm.pager.currentPage === 1}">
        <a ng-click="vm.setPage(vm.pager.currentPage - 1)">Previous</a>
      </li>
      <li ng-repeat="page in vm.pager.pages" ng-class="{active:vm.pager.currentPage === page}">
        <a ng-click="vm.setPage(page)">{{page}}</a>
      </li>
      <li ng-class="{disabled:vm.pager.currentPage === vm.pager.totalPages}">
        <a ng-click="vm.setPage(vm.pager.currentPage + 1)">Next</a>
      </li>
      <li ng-class="{disabled:vm.pager.currentPage === vm.pager.totalPages}">
        <a ng-click="vm.setPage(vm.pager.totalPages)">Last</a>
        </li>
      </ul>
    </div>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
</body>
</html> 

app.js

(function () {
    'use strict';

    var app = angular
        .module('testApp', [])
        .factory('PagerService', PagerService);

    //controllers
        app.controller('searchBtnCtrl', ['$scope', '$http', 'PagerService', function ($scope, $http, PagerService) {
        $scope.performSearch = function() {
            console.log('clicked');
            $('#searchResults').html('');

            var vm = this;

            $http.get('https://jsonplaceholder.typicode.com/posts').then(function (response) {
                $scope.responseData = response.data;
                console.log($scope.responseData.length);
                console.log($scope.responseData);

                vm.itemsToDisplay = $scope.responseData; // array of items to be paged
                vm.pager = {};
                vm.setPage = setPage;

                initController();

                function initController() {
                    // initialize to page 1
                    vm.setPage(1);
                }

                function setPage(page) {
                    if (page < 1 || page > vm.pager.totalPages) {
                        return;
                    }

                    // get pager object from service
                    vm.pager = PagerService.GetPager(vm.itemsToDisplay.length, page);

                    // get current page of items
                    vm.items = vm.itemsToDisplay.slice(vm.pager.startIndex, vm.pager.endIndex + 1);
                }

            });
        }
    }]);

    function PagerService() {
        // service definition
        var service = {};

        service.GetPager = GetPager;

        return service;

        // service implementation
        function GetPager(totalItems, currentPage, pageSize) {
            // default to first page
            currentPage = currentPage || 1;

            // default page size is 10
            pageSize = pageSize || 10;

            // calculate total pages
            var totalPages = Math.ceil(totalItems / pageSize);

            var startPage, endPage;
            if (totalPages <= 10) {
                // less than 10 total pages so show all
                startPage = 1;
                endPage = totalPages;
            } else {
                // more than 10 total pages so calculate start and end pages
                if (currentPage <= 6) {
                    startPage = 1;
                    endPage = 10;
                } else if (currentPage + 4 >= totalPages) {
                    startPage = totalPages - 9;
                    endPage = totalPages;
                } else {
                    startPage = currentPage - 5;
                    endPage = currentPage + 4;
                }
            }

            // calculate start and end item indexes
            var startIndex = (currentPage - 1) * pageSize;
            var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

            // create an array of pages to ng-repeat in the pager control
            var pages = _.range(startPage, endPage + 1);

            // return object with all pager properties required by the view
            return {
                totalItems: totalItems,
                currentPage: currentPage,
                pageSize: pageSize,
                totalPages: totalPages,
                startPage: startPage,
                endPage: endPage,
                startIndex: startIndex,
                endIndex: endIndex,
                pages: pages
            };
        }
    }



})();

And here's a code pen to the project. For this example, ignore the searchbox and just press the search button to active the script.

https://codepen.io/code_nick/pen/gvQJLX?editors=1010

and also the debug version. If you view this version with the console open, you'll see that it's counting the number of items returned (good) and returning the data as objects (also good), it's just not showing up on the page (bummer #1) and the pagination isn't working (bummer #2).

https://s.codepen.io/code_nick/debug/gvQJLX/PBMNWxowdewr

I think it's probably a scope issue, but as scope in angular still gives me issues, maybe the data isn't available when the ng-repeat fires in the html?


Solution

  • There were 3 things which were not right.

    1. You need to store this reference of the controller outside the performSearch function.
    2. You shouldn't be replacing the html inside the searchResults to empty
    3. You are using vm.posts in the ng-repeat but vm.items holds the posts

    With all the fixes, the final code should look like this

    JS

    (function() {
      "use strict";
    
      var app = angular.module("testApp", []).factory("PagerService", PagerService);
    
      //controllers
      app.controller("searchBtnCtrl", [
        "$scope",
        "$http",
        "PagerService",
        function($scope, $http, PagerService) {
          var vm = this;
          $scope.performSearch = function() {
            console.log("clicked");
            $(".post").remove();
    
            $http
              .get("https://jsonplaceholder.typicode.com/posts")
              .then(function(response) {
                $scope.responseData = response.data;
                console.log($scope.responseData.length);
                console.log($scope.responseData);
    
                vm.itemsToDisplay = $scope.responseData; // array of items to be paged
                vm.pager = {};
                vm.setPage = setPage;
    
                initController();
    
                function initController() {
                  // initialize to page 1
                  vm.setPage(1);
                }
    
                function setPage(page) {
                  if (page < 1 || page > vm.pager.totalPages) {
                    return;
                  }
    
                  // get pager object from service
                  vm.pager = PagerService.GetPager(vm.itemsToDisplay.length, page);
    
                  // get current page of items
                  vm.posts = vm.itemsToDisplay.slice(
                    vm.pager.startIndex,
                    vm.pager.endIndex + 1
                  );
                }
              });
          };
        }
      ]);
    
      function PagerService() {
        // service definition
        var service = {};
    
        service.GetPager = GetPager;
    
        return service;
    
        // service implementation
        function GetPager(totalItems, currentPage, pageSize) {
          // default to first page
          currentPage = currentPage || 1;
    
          // default page size is 10
          pageSize = pageSize || 10;
    
          // calculate total pages
          var totalPages = Math.ceil(totalItems / pageSize);
    
          var startPage, endPage;
          if (totalPages <= 10) {
            // less than 10 total pages so show all
            startPage = 1;
            endPage = totalPages;
          } else {
            // more than 10 total pages so calculate start and end pages
            if (currentPage <= 6) {
              startPage = 1;
              endPage = 10;
            } else if (currentPage + 4 >= totalPages) {
              startPage = totalPages - 9;
              endPage = totalPages;
            } else {
              startPage = currentPage - 5;
              endPage = currentPage + 4;
            }
          }
    
          // calculate start and end item indexes
          var startIndex = (currentPage - 1) * pageSize;
          var endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);
    
          // create an array of pages to ng-repeat in the pager control
          var pages = _.range(startPage, endPage + 1);
    
          // return object with all pager properties required by the view
          return {
            totalItems: totalItems,
            currentPage: currentPage,
            pageSize: pageSize,
            totalPages: totalPages,
            startPage: startPage,
            endPage: endPage,
            startIndex: startIndex,
            endIndex: endIndex,
            pages: pages
          };
        }
      }
    })();
    

    HTML

    <html ng-app="testApp">
    
    <head>
      <meta charset="utf-8" />
      <title></title>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    </head>
    
    <body>
      <div class="contentWrapper">
        <div data-ng-controller="searchBtnCtrl as vm">
          <h3>Search:</h3>
          <input id="searchQuery" type="text" placeholder="enter your search query here" data-ng-model="searchInput" />
    
          <button id="searchBtn" data-ng-click="performSearch()">SEARCH</button>
    
          <div id="searchResults">
            <!-- items being paged --> 
            <div ng-repeat="post in vm.posts"> 
              <span class="post"><a href='{{post.title}}' alt='{{post.title}}'>{{post.title}}</a><br />{{post.body}}</span></div>
    
            <!-- pager -->
            <ul ng-if="vm.pager.pages.length" class="pagination">
              <li ng-class="{disabled:vm.pager.currentPage === 1}">
                <a ng-click="vm.setPage(1)">First</a>
              </li>
              <li ng-class="{disabled:vm.pager.currentPage === 1}">
                <a ng-click="vm.setPage(vm.pager.currentPage - 1)">Previous</a>
              </li>
              <li ng-repeat="page in vm.pager.pages" ng-class="{active:vm.pager.currentPage === page}">
                <a ng-click="vm.setPage(page)">{{page}}</a>
              </li>
              <li ng-class="{disabled:vm.pager.currentPage === vm.pager.totalPages}">
                <a ng-click="vm.setPage(vm.pager.currentPage + 1)">Next</a>
              </li>
              <li ng-class="{disabled:vm.pager.currentPage === vm.pager.totalPages}">
                <a ng-click="vm.setPage(vm.pager.totalPages)">Last</a>
              </li>
            </ul>
          </div>
        </div>
      </div>
    
      <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
    </body>
    
    </html>