server side pagination in Angular JS - javascript

I am trying to implement server side pagination in Angular JS .I have created my server response consisting of results and a variable called isMoreResults . When isMoreResults is true I can safely assume that it has more results to be displayed . Now how can I go about creating a pagination on angular Js side ,such that I can display PREVIOUS and NEXT . when user clicks on next I can call the server to fetch next response .Any leads on how can I implement and what is the correct format to implement pagination ? I have went through many sites and majoritily I could see client side validation .should I also switch to client side validation ?

You can do something like this.
angular.module('app', ['ui.bootstrap']);
angular.module('app').controller('PaginationDemoCtrl', function($scope, $http) {
$scope.currentPage = 1;
$scope.limit= 10;
$scope.tracks = [];
getData();
function getData() {
$http.get("https://api.spotify.com/v1/search?query=iron+&offset="+($scope.currentPage-1)*$scope.limit+"&limit=20&type=artist")
.then(function(response) {
$scope.totalItems = response.data.artists.total
angular.copy(response.data.artists.items, $scope.tracks)
});
}
//get another portions of data on page changed
$scope.pageChanged = function() {
getData();
};
});
in html
<div ng-controller="PaginationDemoCtrl">
<h4>Sample Server Pagination</h4>
<uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()" items-per-page="100"></uib-pagination>
</div>

Usually do this for pagination:
1 - Create an API that takes how many SKIP and how many TAKE .. somehting like:
http://www.test.com/api/Users?skip=0&take=10
Then:
2 - in Angular install this plugin: (maybe with bower or npm)
https://github.com/brantwills/Angular-Paging
3 - in your HTML something like:
<paging page="currentPage"
page-size="pageSize"
total="total"
paging-action="DoPaging(page, pageSize, total)">
</paging>
4 - In your Controller:
/**
* MainCtrl - controller
*/
"use strict";
angular
.module('demo')
.controller('UsersCtrl', [
"$scope",
"User",
"$state",
"$timeout",
function (
$scope,
User,
$state,
$timeout) {
var vm = this;
// Binded Functions
$scope.currentPage = 1;
$scope.pageSize = 10;
$scope.DoPaging = _doPaging;
// Page Load
init();
/////////////////////////////////
// PRIVATE FUNCTION
////////////////////////////////
//Page Load
function init() {
$scope.promUsers = User.GetUsers(0, 10).$promise.then(function (resp) {
vm.users = resp;
$scope.total = resp[0].total //<-- put in your Back End Entities a field where you store how many record you have on your DB (all)
}).catch(function (err) {
console.log(err);
});
}
function _doPaging(text, page, pageSize, total) {
var skip = pageSize * (page -1);
User.GetUsers(skip, pageSize).$promise.then(function(resp){
vm.users = resp;
$scope.total = resp[0].total
}).catch(function (err) {
console.log(err);
});
}
////////////////////////////////
}]);
5 - In your service:
"use strict";
angular
.module("demo")
.factory("User", [
"$resource",
"$q",
"enviroment",
"$timeout",
function (
$resource,
$q,
enviroment,
$timeout) {
// Private Filed
var _serviceBase = "api/User/";
// Private Method
//skip and take in query string
var resource = $resource(enviroment.apiUrl + _serviceBase, { skip: '#skip', take: '#take' }, {
query: { method: "GET", isArray: true },
create: { method: "POST", isArray: false },
update: { method: "PUT", isArray: false }
});
// Public Method
return {
GetUsers: function (skip, take) {
return resource.query({ skip: skip, take: take });
}
};
}]);

Related

ngTable: Cannot read property "page"?

I am trying to implement ngTable to display json from rest call. In my factory js, I defined the method for the http get request to obtain all records in this case.
ristoreApp.factory("fmFactory", ['$http', '$window',
function ($http, $window) {
var service = {};
service.getAll = function () {
var url = SERVER + "/ristore/foundation/";
return $http({
headers: {'Authorization': 'Bearer ' + $window.localStorage.getItem("access_token")},
url: url,
method: 'GET',
crossOrigin: true
})
}
return service;
}]);
ngTable is set up in my controller js
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
fmFactory.getAll().success(function (data) {
$scope.reports = data;
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
total: $scope.reports.length, // length of data
getData: function ($defer, params) {
$defer.resolve($scope.reports.slice((params.page() - 1) * params.count(), params.page() * params.count()));
}
});
})
}
}
}]
)
Nothing fancy, just simple pagination with 10 records per page. However, I got error TypeError: Cannot read property 'page' of undefined for the method params.page() in $defer.resolve(). This is very strange. Apparently 'page' is defined in the parameter section of NgTableParams. Why does it complain it is not defined?
EDIT:
Based on the link from Sergii's answer I removed the $defer and changed my controller js to the following:
ristoreApp.controller("fmCtrl",
['$scope', 'fmFactory', 'NgTableParams', function($scope, fmFactory, NgTableParams) {
$scope.selection = '0';
$scope.reports = [];
$scope.fmSearch = function () {
if ($scope.selection == '0') {
$scope.tableParams = new NgTableParams({
page: 1, // show first page
count: 10 // count per page
}, {
getData: function (params) {
return fmFactory.getAll().then(function(data) {
params.total(data.inlineCount);
return data;
});
}
});
}
}
}]
)
And yet nothing displayed but a bunch of lines. The http call url has been tested and returns correct promise using rest api tester.
As I wrote in comment params is undefined, but exception was wrapped\processed by angular that is reason of partly correct exception information.
I believe this problem appears because of you are using newest ng-table-1.0.0 library for now. If you navigate to Angular ngTableDynamic example:server-side list or Angular ngTable example:server-side list please pay attantion that API to load data was changed.
getData: function(params) {
// ajax request to api
return yourFactory.get(params.url()).$promise.then(function(data) {
params.total(data.inlineCount);
return data.results;
});
}
In your parameter $defer also different object (object is params). If you'll try apply provided solution, please make sure that you changed correctly parameters:
params.url() - should be pagination filter like {page: 0, size: 10}
data.inlineCount - total elements on server side
data.results - data list from server side
I hope my investigation helped not only me to fix this problem.

AngularJS: Data could not be saved

Currently I am trying to save an updated function using angularJS. Till now i can edit the data, the data are can be updated on the database side, but its not showing in the frontend side. Unless i have to logout and login once again to view the updated result. Can i know how to fix this bug.
This is my controller.js code:
.controller('FilmDetailController', //havent done yet
[
'$scope',
'dataService',
'$routeParams',
'$location',
'$window',
'UserInfo',
function ($scope, dataService, $routeParams, $location,$window, UserInfo){
//var userName=dataService.getSessionService('user');
if(UserInfo.loggedIn){
$scope.film = [ ];
$scope.filmCount = 0;
var getFilmDetail = function (moviecode) {
dataService.getFilmDetail(moviecode).then(
function (response) {
$scope.film = response.data.ResultSet.Result;
//$scope.userLoginEmail = dataService.getSessionService('userEmail');
$scope.showSuccessMessage = true;
$scope.successMessage = "Film Success";
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
); // end of getStudents().then
};
$scope.editedFilm = {};
$scope.save_note = function ($event,film) {
$scope.editedFilm = film;
dataService.saveNote($scope).then(
function (response) {
// getFilmDetail();
$window.location.href = '#/movieList';
//$window.location.reload();
console.log("done");
},
function (err){
$scope.status = 'Unable to load data ' + err;
}
);
// $scope.reloadRoute = function () {
// $location.path('/movieList');
// $window.location.reload()
// }//end of reload route fnction
}
// only if there has been a courseid passed in do we bother trying to get the students
if ($routeParams && $routeParams.filmid) {
// console.log($routeParams.filmid);
getFilmDetail($routeParams.filmid);
}
}else{
$location.path('/login');
}
}
]
);
Once i click on the save note button the note should be updated in both angular side and also the database side. Currently it can only be updated in the database side except the angular side. Thanks in advance for the help.
From what I can tell it looks like you need to store your note data in a service, and when the note has been successfully saved in your database, you need to push the new note into your service data.
I created a simple fiddle: https://jsfiddle.net/robrothedev/vgbqyv10/
function ItemService() {
var service = {
items: [],
addItem: addItem
};
return service;
function addItem(new_item) {
// run your http request and if the response is valid,
// push the new item to your service array
// $http.post(url,new_item).then(function(response) {
// service.items.push(response.data.new_item);
// });
}
}
function ItemsCtrl(ItemService) {
var vm = this;
vm.items = ItemService.items;
vm.new_item = {};
vm.addItem = addItem;
function addItem() {
ItemService.addItem(vm.new_item);
vm.new_item = {};
}
}

Display values from database AngularJS

I am a novice with Angular. I have a project that a friend and I were working on that acts as a "Reef Tank Controller". It is an arduino/mysql database/angularJS page. My buddy was working on the front end but had to drop out due to work and now I have a semi completed website. From the perspective of the back-end it all works. On the front end I wanted to add a section to control the lighting. I wanted to start out by simply displaying the values of each LED color that is stored in the database. I created a new controller for each LED string I want to display the value of:
'use strict';
/* Controllers */
angular.module('reefController.controllers', ['uiSlider'])
// Main Dashboard Controller
.controller('dashboardController', ['$scope', function($scope) {
$.ajax({
url: 'php/reef_controller_selectVals.php',
type: 'json',
success: function(data) {
reefController.config.data = data;
// Draw Charts
$.each(data.charts, function(name, chartData) {
$(chartData.series).each(function(idx, val){
// Reformat sensor names to their Human readable versions
this.name = reefController.labels[name].sensors[idx];
});
var options = $.extend(reefController.config.chartOptions, {
chart: {
events: {
load: function() {
var chart = this;
setInterval(function() {
$.ajax({
url: 'php/reef_controller_selectVals.php?incremental=' + chart.options.name,
success: function(data) {
// Only add data points if the latest date in the DB is different
if (!(chart.xAxis[0].dataMax == data.timestamp)) {
$(chart.series).each(function(i, series) {
series.addPoint([data.timestamp, data[series.options.id]], true, true);
});
}
}
});
}, reefController.config.timers.chartUpdateInterval);
}
},
renderTo: name
},
name: name,
series: chartData.series,
title: { text: reefController.labels[name].chart.title }
});
var chart = Highcharts.StockChart(options);
reefController.config.charts[name] = chart;
});
//Set LED Value Labels
// Set outlets
var i = 0;
$('.outlet').each(function(){
if (!$(this).hasClass('constant')) {
$(this).attr('data-outlet-id', data.outlets[i].id)
.attr('data-reveal-id', 'date-time-modal')
.attr('data-on-time', data.outlets[i].on_time)
.attr('data-off-time', data.outlets[i].off_time);
if (data.outlets[i].active) {
$(this).addClass('active');
} else {
$(this).removeClass('active');
}
i++;
// Bind click event to outlets
$(this).click(function(){
var outlet = $(this);
var id = outlet.attr('data-outlet-id');
var newOutletState = (outlet.hasClass('active')) ? 0 : 1;
// Set datepickers to currently defined DB times
$('#on_time').val(outlet.attr('data-on-time'));
$('#off_time').val(outlet.attr('data-off-time'));
// Formatter function for date time pickers
var dateFormatter = function(elem, current_time) {
elem.html(moment(current_time).format('ddd M/D/YY HH:mm'));
};
$('#on_time').datetimepicker({
format: 'Y-d-m H:i:s',
inline: true,
defaultDate: outlet.attr('data-on-time'),
onChangeDateTime: function(current_time) { dateFormatter($('#on_time_display'), current_time) },
onGenerate: function(current_time) { dateFormatter($('#on_time_display'), current_time) }
});
$('#off_time').datetimepicker({
format: 'Y-d-m H:i:s',
inline: true,
defaultDate: outlet.attr('data-off-time'),
onChangeDateTime: function(current_time) { dateFormatter($('#off_time_display'), current_time) },
onGenerate: function(current_time) { dateFormatter($('#off_time_display'), current_time) }
});
// Submit Button
$('#submit-btn').click(function() {
data = {
'id': id,
'on_time': $('#on_time').val(),
'off_time': $('#off_time').val(),
'active': newOutletState
};
$.ajax("php/reef_controller_loadOutlet.php", {
type: 'POST',
data: data,
dataType: 'json',
success: function(data) {
outlet.toggleClass('active');
}
});
$('#date-time-modal').foundation('reveal', 'close');
});
// Cancel Button
$('#cancel-btn').click(function(){
$('#date-time-modal').foundation('reveal', 'close');
});
});
}
});
}
});
}])
.controller('outletController', ['$scope', function($scope) {
$.ajax({
url: 'img/outlet.svg',
success: function(svg) {
var svgDoc = document.importNode(svg.documentElement, true);
$('.outlet').append(svgDoc);
}
});
}])
.controller('temperatureController', ['$scope', function($scope) { }])
.controller('phController', ['$scope', function($scope) { }])
.controller('whiteLedCtrl', ['$scope', function($scope) {}])
.controller('blueLedCtrl', ['$scope', function($scope) {}])
.controller('variousColorLedCtrl', ['$scope', function($scope) {}]);
In my dashboard.html file I have:
<table style="width: 100%;">
<tr>
<td>
{{ ledValues }}
</td>
</tr>
<tr>
<td colspan="3" style="background: #eff4f6;"><input type="checkbox" name="overrideLightingSchema" value="overRide">
Override Current Lighting Pattern
</td>
</tr>
<tr>
<td colspan="3">
<select name="lightingSchemas">
<option value="feedingLightingPattern">Feeding Schema</option>
<option value="morningLightingPattern">Morning Schema</option>
<option value="standardLightingPattern">Standard Schema</option>
<option value="twilightLightingPattern">Dusk Schema</option>
<option value="nightTimeLightingPattern">Night Schema</option>
<option value="deepNightLightingPattern">Late Night Schema</option>
</select>
</td>
</tr>
</table>
Sometimes this displays the values from the database other times it just says:
{{ ledValues }}
It may be an async issue of some sort but my angular, well JS for that matter, is weak. Any help would be great.
The main issue I can see here is that you are using $ajax to make requests to the server.
You use the response from the server to set your variable...
reefController.config.data = data;
However since $ajax is not part of Angular this update occurs outside of the scope digest. Therefore Angular does not know to update the binding. You could try wrapping you assignment in $apply.
$scope.$apply(function(){
reefController.config.data = data;
});
That said, I cannot see where reefController is defined. You probably want to be assigning it to the scope:
$scope.$apply(function(){
$scope.MyData = data;
});
However, I would actually recommend you replace the $ajax calls with the Angular $http service.
//inject $http
.controller('dashboardController', ['$scope', '$http', function($scope, $http) {
//then use it later on
$http({
method: 'GET',
url: 'php/reef_controller_selectVals.php'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
$scope.MyData = data;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Example
Below Is a very quick example of how to use $http to get the data from the server.
The full example, including a fake service (that does not require a server response) can be found here: http://codepen.io/anon/pen/GogWMV
'use strict';
angular.module('reefApp', [ 'uiSlider']);
/* CONTROLLER */
angular.module('reefApp')
.controller('dashboardController', dashboardController);
/* Define controller dependencies.
Note how we do not use $scope as we are using controller as vm syntax
And we assign our scope variables to 'ctrl' rather than scope directly.
see john papa styleguide (https://github.com/johnpapa/angular-styleguide#style-y032)
*/
dashboardController.$inject = ['ledService'];
function dashboardController(ledService)
{
var ctrl = this;
//define an array to hold our led data
ctrl.ledData = [];
//call method to get the data
getLedData();
//this method uses our service to get data from the server
//when the response is received it assigns it to our variable
//this will in turn update the data on screen
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = response.data;
});
}
}
/* LED SERVICE */
/* the service is responsible for calling the server to get the data.
*/
angular.module('reefApp')
.factory('ledService', ledService);
ledService.$inject = ['$http'];
function ledService($http)
{
var endpointUrl = "http://addressOfYourEndpoint";
/* expose our API */
var service = {
getLedData: getLedData,
}
return service;
function getLedData()
{
/* this is how you would call the server to get your data using $http */
/* this will return a promise to the calling method (in the controller)
when the server returns data this will 'resolve' and you will have access to the data
in the controller:
Promises: http://andyshora.com/promises-angularjs-explained-as-cartoon.html*/
return $http.get(endpointUrl);
}
}
Taking this further, best practice would be to hold a reference to the data returned from the server inside the service; one reason is the service is a singleton - so this data service and it's data can be shared across controllers.
function ledService($http)
{
var endpointUrl = "http://addressOfYourEndpoint";
/* expose our API */
var service = {
ledData: [],
getLedData: getLedData,
}
return service;
function getLedData()
{
return $http.get(endpointUrl)
.then(function(response)
{
/* assign response to service variable, before promise is returned to caller */
service.ledData = response.data;
});
}
}
Then in our controller...
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = ledService.ledData;
});
}
Update
To collect more data, you could add a service for each piece of data you want to collect - or add more methods to existing service. Let's assume you add a phService.
You then inject this into your controller. And add call a new method to use the service to the data and assign to the model. It can then be shown in the view.
dashboardController.$inject = ['ledService', 'phService'];
function dashboardController(ledService, phService)
{
var ctrl = this;
//our data will be stored here
ctrl.ledData = [];
ctrl.phData = [];
//call methods to get the data
getLedData();
getPhData();
function getLedData()
{
ledService.getLedData()
.then(function(response){
ctrl.ledData = response.data;
});
}
function getPhData()
{
phService.getPhData()
.then(function(response){
ctrl.phData = response.data;
});
}
}
Then in the view (HTML):
<tr ng-repeat="ph in ctrl.phData">
<td> PHValue</td>
<td >
{{ ph }}
</td>
</tr>

use angular factory to hold a value for rest of application to access to minimize server calls

I have the following factory:
angularModule
.factory('ArticleCategoryService', function ($http, $q) {
// Service logic
// ...
var categories = [];
var _getCategories = $http.get('/api/articles/category').success(function (_categories) {
categories = _categories;
});
// .error( function (data, status, headers, config) {
// });
// Public API here
return {
getCategories: function () {
var deferred = $q.defer();
deferred.resolve(_getCategories);
return deferred.promise;
}
};
});
and this is the section that calls this service in the controller:
// Calls the getCategories function from the ArticleCategory Service,
// Will return a promise
ArticleCategoryService.getCategories()
.then(function (categoriesResult) {
$scope.categories = categoriesResult.data;
}, function (err) {
console.log(err);
});
This works but there will be a GET call to the server every time user comes back to this view/state and the categories object that belongs to the factory is never used.
I'm trying to make it so that it will return the categories variable in the factory singleton, and have it initialize on site load (or from first GET call).
But if I just return categories when user calls getCategories, it will return nothing since we need time for the $http call.
Check if categories is defined, and resolve the promise with the variable rather than the GET request if it is:
return {
getCategories: function () {
var deferred = $q.defer();
if (categories.length > 0) {
deferred.resolve(categories);
} else {
deferred.resolve(_getCategories);
}
return deferred.promise;
}
};
I'm doing exactly same thing on my app. I have a main module and a main controller within it that wraps any other controllers, so its scope is persistent over views.
In this main controller you could assign the factory's getCategory() data to a scope variable and then you could use that in your entire app, because the scope will be inherited to child scopes.
angularMainModule
.factory('ArticleCategoryService', function ($http) {
var ArticleCategoryServiceMethods = {};
//fn: getCategories
ArticleCategoryServiceMethods.getCategories = function(fromWhere){
return $http.get(fromWhere)
.then(function(res){
return res.data;
}, function(err){
return err.status;
});
}
return ArticleCategoryServiceMethods;
}
angularMainModule
.controller('MAINCTRL', function($scope, ArticleCategoryService) {
//get all categories
$scope.categories = ArticleCategoryService.getCategories('/api/articles/category');
//... the rest of the main ctrl code ... //
}
... when you define the main module, make sure you inject the rest of your modules in it
var angularMainModule = angular.module('angularMainModule', [
'ngRoute',
'ngTouch',
'ngAnimate',
//< ng - etc >,
'Module1',
'Module2',
//< module - n >
]);
...and the markup (i'm bootstrapping my angular app manually, but you could add the ng-app="angularMainModule" attribute on the html tag if you're doing it that way):
<html ng-controller="MAINCTRL">
<!-- head data -->
<body>
<div id="page" ng-view></div>
If you want to make sure data is loaded before your app opens the main page, you could add that service call in the routeProvider block of your app (on the default route), so when the MAINCTRL will be loaded the data will be already there, ready to be assigned.
angularModule
.factory('ArticleCategoryService', function ($http) {
// Service logic
// ...
var categories = [];
$http.get('/api/articles/category').success(function (_categories) {
categories = _categories;
});
// Public API here
return {
categories: categories
};
});
angularModule
.controller('ControllerMain', function($scope, ArticleCategoryService) {
$scope.categories = ArticleCategoryService.categories;
});

Pass $scope object to route, keep if refresh

Lets say i list all users in a list, when i click a user i want to route to a new view and get the data for the selected person.
What is the preferred way? Should i move the data i already got when i listed the users or should i create a new server call?
My first thought is to pass the data, but the problem with this is that the data the gets lost if the user refreshes the page.
What is the best practice to solve this?
Small example:
(function() {
var app = angular.module('app');
var controllerId = 'app.controllers.views.userList';
app.controller(controllerId, [
'$scope', 'UserService',function ($scope, userService) {
var vm = this;
vm.users = [];
userService.getAllUsers().success(function (data) {
vm.users= data.users;
});
var gotoUser = function(user) {
// Pass the user to UserDetail view.
}
}
]);
})();
<div data-ng-repeat="user in vm.users" ng-click="vm.gotoUser(user)">
<span>{{customer.firstname}} {{customer.lastname}}</span>
</div>
i now list the user details in UserDetail view, this view is now vulnerable against a browser refresh.
Typically most people just create a new server call, but I'll assume you're worried about performance. In this case you could create a service that provides the data and caches it in local storage.
On controller load, the controller can fetch the data from the service given the route params and then load the content. This will achieve both the effect of working on page refresh, and not needing an extra network request
Here's a simple example from one of my apps, error handling left out for simplicity, so use with caution
angular.
module('alienstreamApp')
.service('api', ['$http', '$q','$window', function($http, $q, $window) {
//meta data request functions
this.trending = function() {
}
this.request = function(url,params) {
var differed = $q.defer();
var storage = $window.localStorage;
var value = JSON.parse(storage.getItem(url+params))
if(value) {
differed.resolve(value);
} else {
$http.get("http://api.alienstream.com/"+url+"/?"+params)
.success(function(result){
differed.resolve(result);
storage.setItem(url+params,JSON.stringify(result))
})
}
return differed.promise;
}
}]);
I would say that you should start off simple and do a new server call when you hit the new route. My experience is that this simplifies development and you can put your effort on optimizing performance (or user experience...) where you will need it the most.
Something like this:
angular.module('app', ['ngRoute', 'ngResource'])
.factory('Users', function ($resource) {
return $resource('/api/Users/:userid', { userid: '#id' }, {
query: { method: 'GET', params: { userid: '' }, isArray: true }
});
});
.controller("UsersController",
['$scope', 'Users',
function ($scope, Users) {
$scope.loading = true;
$scope.users = Users.query(function () {
$scope.loading = false;
});
}]);
.controller("UserController",
['$scope', '$routeParams', 'Users',
function ($scope, $routeParams, Users) {
$scope.loading = true;
$scope.user = Users.get({ userid: $routeParams.userid }, function () {
$scope.loading = false;
});
$scope.submit = function () {
$scope.user.$update(function () {
alert("Saved ok!");
});
}
}]);
.config(
['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/users', {
templateUrl: '/users.html',
controller: 'UsersController'
})
.when('/users/:userid', {
templateUrl: '/user.html',
controller: 'UserController'
})
.otherwise({ redirectTo: '/users' });
}
]
);

Categories

Resources