Angular JS: Increment a Counter in a Service From Controller - javascript

I'm trying to fetch a value from an API inside my service for a counter, and then increment on that counter inside my controller:
service
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL;
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
var total = response.data.meta.total;
return total;
}, function(error) {
console.log("error occured");
})
},
incrementCounter: function(){
total++;
}
}
}
controller
app.controller('Controller1', function ($scope, MyService) {
var params = {
...
}
MyService.fetchTotal(params).then(function(response) {
$scope.counter = response;
});
$scope.incrementCounter = function(){
MyService.incrementCounter();
}
});
view
<div ng-controller="Controller1">
{{ counter }}
<span ng-click="incrementCounter()">increment</span>
</div>
I can't work out how increment on that total I get back from the API with an ng-click. Is this possible?
Any help is appreciated. Thanks in advance!

Angularjs services are singletons, so you can create variables and share it among controllers.
Notice below how variable total is initialized in the service. Same variable is used for interaction with the controllers by using the getCounter() method.
Also notice the init() method. Your controller can call MyService.init first thing to initialize the total variable.
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL';
var total = 0; /* or some other initial value*/
return {
init: function(){
$http.get(url, { params: p })
.then(function(response) {
total = response.data.meta.total;
}, function(error) {
console.log("error occured");
});
},
incrementCounter: function(){
total++;
},
getCounter: function(){
return total;
}
}
}
See plunker for demo: http://plnkr.co/edit/idSxgm0axk43ydThTbJF?p=preview

Looks like you are doing everything right, except the variable total.
You should initialize it in an upper level so that it is also visible from incrementCounter function:
function MyService($http) {
var url = 'URL;
var total = 0;
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
total = response.data.meta.total;
return total;
}, function(error) {
console.log("error occured");
})
},
incrementCounter: function(){
total++;
}
}
}
Hope it helps

Related

How to use angular 1.6.5 call $http request recursively when data set returned is limited

I would like to use Angular 1.6.5 for a project rebuild, but I'm not sure how to use the $http.get request in a factory when the source returns only a limited number of records at a time (1000 returned per request) and there are over 2000 records that I need to get.
In my current code I use jquery ajax and in the .done method I check for the presence of the value "__next", and if it exists, I recall the function passing the value "__next". When the "__next" value isn't returned, I do something with the data.
function getSpecifiedList(url){
var specUrl = url;
$.ajax({
url: specUrl,
type: "GET",
headers:{"accept":"application/json;odata=verbose",
error: function(xhr){
console.log(xhr.status + " " + xhr.statusText);
}
}
}).done(function (results){
$("#wc_report_holder").text(results.length);
//buildObjects processes the results and adds to an array
buildObject(results);
if(results.d.__next){
getSpecifiedList(results.d.__next);
}else{
buildGridView();
}
}).fail(function(error){
$("#wc_report_holder").text("There was an error: " + error);
});
}
I would like to figure out how to implement that same value check and recursive call in angular 1.6.5 using best practice and most efficient but I haven't had luck figuring it out based on the angular docs and Googling.
Here is a short version of what I currently have using Angular 1.6.5.
<script>
var sitesApp = angular.module("sitesApp", ['ngRoute']);
sitesApp.controller('SitesListCtrl', ['$scope', 'sites',
function ($scope, sites) {
sites.list().then(function (response) {
$scope.sites = response.data.value;
});
}
]);
sitesApp.controller("SiteDetailsCtrl", ['$scope', '$routeParams', 'sites',
function ($scope, $routeParams, sites) {
sites.find($routeParams.SiteCodePc, function (site) {
$scope.site = site;
});
}
]);
sitesApp.config(function ($routeProvider, $locationProvider) {
$locationProvider.hashPrefix('!');
$routeProvider.
when('/', {
templateUrl: 'https://machine/sites/site-list.html',
controller: 'SitesListCtrl'
}).
when('/:SiteCodePc', {
templateUrl: 'https://machine/sites/site-details.html',
controller: 'SiteDetailsCtrl'
}).
otherwise({
redirectTo: '/'
});
});
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
var cachedData;
function getData(callback) {
if (cachedData) {
callback(cachedData);
} else {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
//HERE IS WHERE I THINK THE SOLUTION NEEDS TO BE IMPLEMENTED
cachedData = response;
return cachedData;
});
}
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
</script>
<div ng-app="sitesApp">
<div ng-view></div>
</div>
Thanks in advance
It looks like you can do a simple recursion where you accept a second (optional) parameter. If you are calling getData() for the first time then you can get your first 1000 results. However if you find __next then you will call it again sending the current 1000 results you have and concat the next 1000 results with the previous 1000.
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
function getData(callback, results) {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
// If you have found a previous batch of results then concat the two arrays
if(results) {
response = response.concat(results);
}
// If there are more results to be found then recursively call the same function passing the batched results
if(response.__next) {
return getData(callback, response);
}
// If there are no more results to be found then trigger your callback function
else {
callback(response);
}
});
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
I have implemented same kind of scenario with pagination logic and $q. In this sample code I am pulling the records recursively as lazy based on the LazyloadingLimit. You can specify the limit based on your requirement.So it only pulls the records based on the count from the total collection. In this below sample I am not using $http. On your real sample you can use the $http to pull the records from the server. Here I just hard coded the collection initially.
In your case you have to fetch total records count initially and apply some pagination logic or some other parameter to pull the next records.
angular.module('app', []);
angular.module('app').controller('SampleController', function ($scope,$http, $timeout, $q) {
// $scope.initialize();
$scope.mainCount = 0;
$scope.lazyloadingLimit = 2;
$scope.tileDefinitions = null;
$scope.tempList = null;
$scope.totalRecordCollection = [
{ "Name": "Record1" },
{ "Name": "Record2" },
{ "Name": "Record3" },
{ "Name": "Record4" },
{ "Name": "Record5" },
{ "Name": "Record6" },
{ "Name": "Record7" },
];
function getTotalRecordCollection() {
var deferred = $q.defer();
deferred.resolve($scope.totalRecordCollection);
return deferred.promise;
}
$scope.initialize = function () {
debugger;
var currentCount=0;
var pageList = new Array();
var currentPage = 1;
var numberPerPage = 2;
var numberOfPages = 0;
function makeList() {
numberOfPages = getNumberOfPages();
}
function getNumberOfPages() {
return Math.ceil($scope.tempList.length / numberPerPage);
}
function nextPage() {
currentPage += 1;
}
function loadList() {
var deferred = $q.defer();
if (currentCount !== $scope.tempList.length) {
var begin = ((currentPage - 1) * numberPerPage);
var end = begin + numberPerPage;
pageList = $scope.tempList.slice(begin, end);
currentCount = currentCount + pageList.length;
$scope.mainCount = currentCount;
deferred.resolve(true);
} else {
debugger;
return $q.reject();
}
return deferred.promise;
}
function loadNextRecords() {
loadList().then(function (res) {
nextPage();
loadNextRecords();
});
}
getTotalRecordCollection().then(function (response) {
debugger;
$scope.tempList = response;
makeList();
loadNextRecords();
});
}
});
<body ng-controller="SampleController">
<input type="button" value="Click Here" ng-click="initialize()"/>
{{mainCount}}
</body>
Once all the records are loaded , you should reject the promise else the recursive loops never end.
Hope this helps

Angular JS / articles is not defined

I have this code in services.js in in my Angular App:
.factory('Articles', function($http) {
$http.get('https://api.myjson.com/bins/4ici6').then( function(response) {
var articles = response.data.articles;
});
return {
all: function() {
return articles;
},
get: function(articleId) {
for (var i = 0; i < articles.length; i++) {
if (articles[i].id === parseInt(articleId)) {
return articles[i];
}
}
return null;
}
};
})
It doesn't work as I get this error in the console:
ReferenceError: articles is not defined
at Object.all (http://localhost:8100/js/services.js:31:14)
at new <anonymous> (http://localhost:8100/js/controllers.js:4:30)
at Object.instantiate (http://localhost:8100/lib/ionic/js/ionic.bundle.js:18015:14)
Also here is the controller.js code that refers to articles:
.controller('NewsCtrl', function($scope, Articles) {
$scope.articles = Articles.all();
})
.controller('NewsDetailCtrl', function($scope, $stateParams, Articles) {
$scope.article = Articles.get($stateParams.articleId);
$scope.posttofacebook = function (){
window.plugins.socialsharing.shareViaFacebook(null, null, $scope.article.url);
}
$scope.posttotwitter = function (){
window.plugins.socialsharing.shareViaTwitter(null, null, $scope.article.url);
}
})
What am I doing wrong here?
Because $http.get is an asynchronous call you'll have to deal with that.
Just putting it on top of your factory won't consistently work.
Try this instead:
.factory('Articles', function($http) {
return {
all: function() {
return $http.get('https://api.myjson.com/bins/4ici6').then(function(response) {
return response.data.articles;
});
},
get: function(articleId) {
//Probably best here to call an API endpoint that will return a single article with the parameter's articleId
//Something along the lines of
//$http.get('https://api.myjson.com/bins/4ic16/' + articleId).then(function(response) { //handle response});
}
};
})
Your controller should also be changed to handle the async function call:
.controller('NewsCtrl', function($scope, Articles) {
Articles.all().then(function(articles) { $scope.articles = articles });
})
You have your articles variable declared inside the callback of the http, so it won't be available outside of it. Move it outside:
.factory('Articles', function($http) {
// declaring it here makes it available in the 'all' function
var articles = [];
$http.get('https://api.myjson.com/bins/4ici6').then( function(response) {
articles = response.data.articles;
});
return {
all: function() {
return articles;
},
get: function(articleId) {
for (var i = 0; i < articles.length; i++) {
if (articles[i].id === parseInt(articleId)) {
return articles[i];
}
}
return null;
}
};
})
But because you fetch your articles asynchronously through http, it can happen that you do the Articles.all() before the articles are fetched, resulting in an empty array. Instead, I would do it like this:
.factory('Articles', function($http, $q) {
// declaring it here makes it available in the 'all' function
var articles = [];
var fetched = false;
var getAll = function() {
var deferred = $q.defer();
if (!fetched) {
$http.get('https://api.myjson.com/bins/4ici6').then( function(response) {
articles = response.data.articles;
fetched = true;
deferred.resolve(articles);
});
} else {
deferred.resolve(articles);
}
return deferred.promise;
}
return {
all: getAll,
get: function(articleId) {
var deferred = $q.defer();
getAll().then(function(articles) {
for (var i = 0; i < articles.length; i++) {
if (articles[i].id === parseInt(articleId)) {
deferred.resolve(articles[i]);
break;
}
}
// not found
return deferred.reject();
}
return deferred.promise;
}
};
})
And use it like this:
Articles.all().then(function(articles){
// now the articles are available
});
Articles.get(id).then(function(article){
// found
}, function(){
// not found
});

how do i get the service response data into the md dialog angularjs?

i have created the custom service like this
app.service('userService', function($http,UrlService) {
return {
init: function(callback) {
$http.get(UrlService.baseUrl +'/api/users/list').then(function(user_response) {
callback(user_response);
});
}
}
})
Inside of my project main controller i have used like this to get the angular material design modal.
$scope.replyComplaint = function(user,complaint_id) {
complaint_id=user._id;
console.log(complaint_id)
$mdDialog.show({
controller: DialogCtrl,
templateUrl: 'submodules/user_management/replydialog.html',
resolve: { complaint_id : function() {return complaint_id;} },
locals: {
users: $scope.users
},
parent: angular.element(document.body),
clickOutsideToClose: true,
})
.then(function(response) {
$scope.response = response;
console.log(response);
}, function() {
//fail
});
};
created another controller for dialog as in the angular material docs as follows
function DialogCtrl($scope, $rootScope, $mdDialog, users,complaintService, UrlService, $http) {
complaintService.init(function(complaint_response) {
$scope.complaints = complaint_response.data;
$scope.getUsers();
});
$scope.getUsers = function(complaint_id) {
console.log(complaint_id);
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.replyMail = function(complaint_id) {
console.log(complaint_id);
$http.post(UrlService.baseUrl + '/api/complaints/complaint/'+complaint_id , {
complaint: "replyText"
}, $scope)
.then(function(response) {
console.log(name);
$state.reload();
}, function(response) {
console.log(name);
});
}
}
}
Now, i need to get the user_response data in DialogController. if i put console.log('$scope.users') inside of this userservice.init function, i can get the data. but not outside of it. how to get the response data outside of the userService.init function
userService.init(function(user_response) {
$scope.users = user_response.data;
}); //this is added in DialogController
Main intension is to get the user.comlaint_id in the post request of reply mail function . that user.complaint_id is a part of the user_response
Anyone please help me. Thanks
The $http.get call returns a promise, you can just use that.
app.service('userService', function($http,UrlService) {
return {
init: function(callback) {
return $http.get(UrlService.baseUrl +'/api/users/list');
}
}
});
Controller:
function Dialog($scope,$rootScope, $mdDialog,userService,UrlService,$http) {
// console.log(userService.init());
init();
function init() {
userService.init().then(function(response) {
$scope.users = response.data;
});
}
}
This also has the advantage of easier error handling:
function Dialog($scope,$rootScope, $mdDialog,userService,UrlService,$http) {
// console.log(userService.init());
init();
function init() {
userService.init().then(function(response) {
$scope.users = response.data;
}, function(error) {
// handle error
});
}
}
You should read up on angular/javascript promises and their chaining mechanism: angular promises
Here is the solution
userService.init(function(user_response) {
$scope.users = user_response.data;
$scope.init();
});
$scope.init = function() {
You can access $scope.users here
}
Call any method instead of init() in which you require $scope.users

Angular - pass info from array from one controller to another

I'm having a little problem trying to pass a service within controllers.
What I'm trying to do is a shopping cart, I have a list of items and when I hit a button, those items get added to the cart, then I want to list those items in the cart in a separate page using a separate controller, so I'm trying to use a factory for the cart, but I don't know if you can set a factory object within a controller.
Here's my code, hope you can point me in the right direction.
var app = angular.module("Shop", []);
app.factory('DataService', function () {
var cart = [];
var set = function (data) {
cart = data;
}
var get = function () {
return cart;
}
});
app.controller("catalogController", function ($scope, $http) {
$scope.bookStore = {
selected: {},
books: null
};
$scope.cart = [];
$http.get("json/books.json")
.success(function (data) {
console.log(data);
$scope.bookStore.books = data;
})
.error(function (err) {
});
$scope.addToCart = function (book) {
var found = false;
$scope.cart.forEach(function (item) {
if (item.id === book.id) {
item.quantity++;
found = true;
}
});
if (!found) {
$scope.cart.push(angular.extend({
quantity: 1
}, book));
}
};
$scope.removeFromCart = function (item) {
var index = $scope.cart.indexOf(item);
$scope.cart.splice(index, 1);
};
$scope.getCartPrice = function () {
var total = 0;
$scope.cart.forEach(function (product) {
total += product.price * product.quantity;
});
return total;
};
});
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService;
});
Change things a bit to something like:
app.factory('DataService', function () {
var cart = [];
return {
set: function (data) {
cart = data;
},
get: function () {
return cart;
},
add: function (item) {
cart.push(item);
}
}
});
...
app.controller("checkoutController", function ($scope, DataService) {
$scope.cart = DataService.get();
});
And then move the $http.get method and all the operations on the card in the other controller to functions in the factory and declare them on the same way as the above Dataservice.get()
You should do something like this:
A service is a singleton in angular js, that's mean you only have one instance of this class in your app.
var app = angular.module("Shop", []);
app.factory('DataService', function ($http) { // usualy your service is the one which call your API (not your controller)
var cart = null; // the cart array is set in the instance of the class as private
return{ // here you declare all the functions you want to call from outside (your controllers)
set : function (data) {
cart = data;
},
get: function(){
return cart;
},
getFromAPI = function () { // the code you have in your controller should goes here
return $http.get("json/books.json")
.success(function (data) {
console.log(data);
cart = data; //now you set you cart variable
})
.error(function (err) {
});
},
});
Then in your controllers:
app.controller("catalogController", function ($scope, DataService) { // include your service as a dependency
$scope.bookStore = {
selected: {},
books: null
};
$scope.cartInCatalogController = DataService.get(); // it will set the value of cart that's in your service to your controller's scope
if(!$scope.cartInCatalogController) {// if it's null so call the API
DataService.getFromAPI()// this function should return a promise
.success(function(data){// so call the success function
$scope.cartInCatalogController = data;
})
.error(function(error){
// do something here if you want
});
});
You can do the same in your other controller.
About the addToCard function and other stuff I let you find it by yourself.
You can start from here :)

AngularJS pass a variable into service that use $http + $interval

Found this code while struggling with $http and $interval.
http://embed.plnkr.co/fSIm8B/script.js
Forked it to:
http://plnkr.co/edit/Al8veEgvESYA0rhKLn1q
To make it useful, how do I pass a variable to the service?
Broken code to show intent:
var myAppModule = angular.module("myApp", ['ngMockE2E']);
myAppModule.controller('MyController', function($scope, pollingService) {
var stopNow = 5;
var id = 1001;
pollingService(stopNow, id).then(
function(value) {
//fully resolved (successCallback)
$scope.data = value;
console.log('Success Called!');
},
function(reason) {
//error (errorCallback)
console.log('Error:' + reason);
},
function(value) {
//notify (notifyCallback)
$scope.data = value;
console.log('Notify Calls:' + value.count);
}
);
});
myAppModule.factory("pollingService", function ($http, $interval, $q, $httpBackend) {
var data = { resp: {}, status: 'Initialized', count: 0};
var deferred = $q.defer();
$httpBackend.whenGET("data.json").respond({type:'mock'});
//just loop 10 times for an example
var completed = $interval(function(ip) {
data.status = 'Running';
**//How can I Change the $http URL by passing a variable in?**
$http.get('/getId/' + id).then(function(r) {
data.resp = r.data.type;
data.count++;
**//Instead of 5 I want to pass this in as an variable**
if (data.count==stopNow)
{
$interval.cancel(completed);
}
console.log('Http\'s:' + data.count);
deferred.notify(data);
});
}, 500, 10);
completed.then(function(){
data.status = 'Completed!';
deferred.resolve(data);
});
return deferred.promise;
});
You can return a function in your service:
myAppModule.factory("pollingService", function ($http, $interval, $q, $httpBackend) {
return {
doSomething: function(arg1, arg2){
// Your code goes here
return deferred.promise;
}
}
And then on the controller
pollingService.doSomething(arg1,arg2).then(...)

Categories