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?
There were 3 things which were not right.
this
reference of the controller outside the
performSearch
function.searchResults
to emptyvm.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>