Calling second http call after the first http call finish - angularjs

This is the service where im saving the data and returning the result
nurseService.js
(function () {
'use strict';
angular.module('app.services')
.factory('NurseService', NurseService);
NurseService.$inject = ['$http', '$q','Constants'];
function NurseService($http, $q, Constants){
var service = {
saveSample:saveSample
};
return service;
function saveSample(data) {
var deferred = $q.defer();
$http({method:"POST", data:data, url:Constants.API_URL_SAVE_SAMPLE_COLLECATION}).then(function(result){
return deferred.resolve(result.data);
});
};
return deferred.promise;
}
})();
This is the controller where im using the return value and based on the value returned im calling another http get method and printing it.
vm.saveSamples = function() {
var data = {
visitId: visitId,
orders: vm.gridTestApi.selection.getSelectedRows()
};
var url = Constants.API_URL_SAVE_SAMPLE_COLLECATION;
var barCodeResponse = null;
var sampleId = "";
var myDataPromise = NurseService.saveSample(data);
myDataPromise.then(function(result) {
console.log("data.name"+ JSON.stringify(result));
vm.printBarCode(result.sampleId);
// if(sampleId != ""){
printElement("printThisElement");
// }
});
//Barcode method this should call after saving the data and returned the sampleId
vm.printBarCode = function(sampleId) {
$http.get("master/barcode/"+sampleId).then(function (response) {
vm.barCodeImage = angular.copy(response.data.result);
});
}
But here before the saving print is calling. How can I hadle so that the first call should finish before the second http call to barcode and print it
//Print code
function printElement(elem) {
var printSection = document.getElementById('printSection');
// if there is no printing section, create one
if (!printSection) {
printSection = document.createElement('div');
printSection.id = 'printSection';
document.body.appendChild(printSection);
}
var elemToPrint = document.getElementById(elem);
// clones the element you want to print
var domClone = elemToPrint.cloneNode(true);
printSection.innerHTML = '';
printSection.appendChild(domClone);
window.print();
window.onafterprint = function () {
printSection.innerHTML = '';
}
};

You have to return the $http call in printBarCode and use a .then like so:
//Barcode method this should call after saving the data and returned the sampleId
vm.printBarCode = function(sampleId) {
return $http.get("master/barcode/"+sampleId).then(function (response) {
vm.barCodeImage = response.data.result;
});
}
myDataPromise.then(function(result) {
console.log("data.name"+ JSON.stringify(result));
return vm.printBarCode(result.sampleId)
}).then(
function() {
printElement("printThisElement");
},
function(error) {
// error handler
}
);
printElement will now wait for the printBarCode promise and .then to fulfil before executing.
You also don't have to use a $q.defer when doing a $http call, $http is already a promise so you can just return that like so:
function saveSample(data) {
return $http({method:"POST", data:data, url:Constants.API_URL_SAVE_SAMPLE_COLLECATION})
.then(
function(result) {
return result.data;
},
function(error) {
// don't forget to handle errors
}
);
}

First of all, $http internally implements promises you you dont have to explicitly create them.
Secondly, you should put all your http requests in the service/factory
The modified code looks like
angular.module('app.services')
.factory('NurseService', function($http){
var service = {
saveSample : function(data){
//first http implementation here
return $http.post(....);
}
getBarcode : function(sampleId){
//http implementation here for barcode
return $http.get(....);
}
}
return service;
});
and your controller can use the service like
angular.module('app.services')
.controller('saveSampleCtrl',function($scope,NurseService){
var postData = {
//your post data here
}
NurseService.saveSample(postData)
.then(function(data){
//read sampleId here from data
var sampleId = data.sampleId;
NurseService.getBarcode(sampleId)
.then(function(){
//your print statement here
});
});
}
there might be typos in the code but this is just a basic idea on how you could do that. Hope it helps

Related

Call a asynchronous method after another in AngularJS

I have a $watch attached to the appId variable. SO, when appId get changed it called the init function
$scope.$watch('appId', function() {
if ($scope.appId != '') {
console.log('Inside appId watch');
$scope.init();
}
});
init() calls two service methods
$scope.init = function() {
if ($scope.appId === undefined || $scope.appId == '') return false;
$scope.getPrincipals($scope.loadPrincipals);
$scope.getSignaturesByAppId($scope.loadSignatures);
};
The methods are
$scope.getSignaturesByAppId = function (callback) {
ApplicationDataSource.getSignaturesByAppId($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadSignatures = function (result) {
var signatureResultSet = angular.fromJson(result[0]);
$scope.appSignatures = signatureResultSet;
if($scope.appSignatures.length===0){
$scope.setDefaultValue();
}
else{
$scope.setValueFromObject();
}
};
$scope.getPrincipals = function (callback) {
ApplicationDataSource.getApplicationPrincipalList($scope.appId, function (result) {
callback(result);
$scope.$apply();
});
};
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
};
The problem occurs here. In loadSignatures(), I have called a method setDefaultValue() which needs the data retrieve from loadPrincipals. So, when, loadSignatures called, principal data is not updated.
How to call the $scope.getPrincipals($scope.loadPrincipals) after $scope.getSignaturesByAppId($scope.loadSignatures) finish to retrieve data.
You can use Promises, here is an example:
var promise = callThatRunsInBackground();
promise.then(
function(answer) {
// do something
},
function(error) {
// report something
},
function(progress) {
// report progress
});
So in your code it might look like (I will leave it to you to fix as I'm not going to compile or check for syntax errors):
var getPrincipals = $scope.getPrincipals($scope.loadPrincipals);
getPrincipals.then(
function(datapayload) {
//do something with datapayload perhaps
$scope.getSignaturesByAppId($scope.loadSignatures);
});
This will make it wait until getPrincipals is finished before running getSignaturesbyAppId
What solve my issue is, I called $scope.getSignaturesByAppId($scope.loadSignatures); in loadPrincipals callback function, not in init()
$scope.loadPrincipals = function (result) {
var guarantorResultSet = angular.fromJson(result[0]);
$scope.principals = guarantorResultSet;
$scope.getSignaturesByAppId($scope.loadSignatures);
};

AngularJS - Factory from Controller - not getting value from $http.get

I have a newbie question here.
I am coding a factory in angularJS. With it I want to have a list of users, and also a method to fill it.
So this is my code ...
The factory
app.factory("usuariosFactory", function ($http) {
var f = {};
f.users = [];
f.getUsers = function (callback) {
var token = window.localStorage.getItem("_token");
$http.get("http://localhost:8000/api/user/list?token=" + token).then(function (response) {
f.users = response.data.users;
/* the console.log outputs OK with the users from the server */
console.log(f.users);
});
};
return f;
});
The controller
app.controller("usuariosController", function ($scope, usuariosFactory) {
var scope = this;
/* link users from factory to controllerÅ› scope .. NOT WORKING */
usuariosFactory.getUsers();
scope.usuarios = usuariosFactory.users;
});
I am hitting my head to the desk right now. I dont understand how to achieve this.
You should just return the promise from the factory to controller
Then in controller, you should subscribe to that promise and assign data to your scope variable
Factory:
app.factory("usuariosFactory", function ($http) {
var f = {};
f.users = [];
f.getUsers = function (callback) {
var token = window.localStorage.getItem("_token");
return $http.get("http://localhost:8000/api/user/list?token=" + token);
};
return f;
});
Controller:
app.controller("usuariosController", function ($scope, usuariosFactory) {
var scope = this;
usuariosFactory.getUsers().then(function (response) {
scope.usuarios = response.data;
});
});
The usuariosFactory.getUsers is an asynchronous function, due to $http.get inside. So, to have your data, you have to use the callback function that you've already put in the getUsers. The code should be like:
usuariosFactory.getUsers(function () {
scope.usuarios = usuariosFactory.users;
});
and after the f.users = response.data.users; you have to call the callback function. Like this:
f.getUsers = function (callback) {
var token = window.localStorage.getItem("_token");
$http.get("http://localhost:8000/api/user/list?token=" + token).then(function (response) {
f.users = response.data.users;
callback();
});
};
That way you will handle ansynchronous functions with a callback funtion. Another way to do this is with promises, in that way, your code should be like this:
The factory
app.factory("usuariosFactory", function ($http, $q) {
var f = {};
f.users = [];
f.getUsers = function (callback) {
var token = window.localStorage.getItem("_token");
var deferred = $q.defer(); // Creates the object that handles the promise
$http.get("http://localhost:8000/api/user/list?token=" + token)
.then(function (response) {
f.users = response.data.users;
deferred.resolve('You can pass data!'); // Informs that the asynchronous operation have finished
});
return deferred.promise; // Returns a promise that something will happen later
};
return f;
});
The controller
app.controller("usuariosController", function ($scope, usuariosFactory) {
var scope = this;
// Now you can use your function just like you use $http
// This way, you know that watever should happen in getUsers, will be avaible in the function
usuariosFactory.getUsers()
.then(function (data) {
console.log(data) // Print 'You can pass data!'
scope.usuarios = usuariosFactory.users;
});
});

Reuse data from $http in factory

I understand that the appropriate method to share data between controllers in Angular.js is by using Factories or Services.
app.controller('Controller1', function($scope, DataService) {
DataService.getValues().then(
function(results) {
// on success
console.log('getValues onsuccess');
});
});
app.controller('Controller2', function($scope, DataService) {
DataService.getValues().then(
function(results) {
// on success
console.log('getValues onsuccess');
});
});
app.factory('DataService', function($http) {
var getValues = function() {
console.log('making http request');
return $http.get("/api/getValues");
};
return {
getValues: getValues
}
});
I have two controllers calling the same method in a factory twice
and this is perfectly fine and everything is working as it should. My only concer is that it seems a bit unecessary to make the same request twice? Would the use of $broadcast be a better approach?
Or could i structure my code differenty so that the service is called only once?
You could store the results of the request in the factory and retrieve those instead.
app.factory('DataService', function($http) {
var values;
var requestValues = function() {
console.log('making http request');
$http.get("/api/getValues").then(
function(results){
values = results;
});
};
var getValues = function() {
return values;
};
return {
requestValues : requestValues,
getValues: getValues
}
});
If your data is somekind of static and may not change very often over time you could do something like:
app.factory('DataService', function($http) {
self = this;
this.isLoaded = false;
this.results;
this.getValues = function() {
console.log('making http request');
$http.get("/api/getValues").then(
function(results) {
// on success
console.log('getValues onsuccess');
self.isLoaded = true
this.results = results;
return results;
})
);
};
})
And in the controller:
app.controller('Controller2', function($scope, DataService) {
if(!DataService.isLoaded){
results = DataService.getValues()
}else{
results = DataService.results;
}
});
You should consider caching in your DataService. Add a variable to hold the result from the http service and a time-stamp variable to store the time it was retrieved.
If a second call to the service is within a preset time period (lets say, 5 seconds), then http call is not made and data from the cache is returned.
app.factory('DataService', function($http) {
var cachedValue = null;
var lastGet = null;
var getValues = function() {
var timeNow = new Date();
if (cachedValue == null || ((timeNow - lastGet) < 5000)) {
console.log('making http request');
lastGet = timeNow;
cachedValue = $http.get("/api/getValues");
} else console.log('returning cached value');
return cachedValue;
};
return {
getValues: getValues
}
});

TypeError: promises[i].done is not a function

I am returning an $http.get object from a service to a controller.
function in the geturl service -
this.fetch = function(index){
var url = some_url;
return $http.get(url,{timeout:8000});
};
In the controller I have --
var request = geturl.fetch(0);
request.success(function(data,status,headers,config){
// some logic
}).error(function(data,status,headers,config){
// some logic
});
$scope.promise.push(request); // $scope.promise is an array which contains all the promises
whenAll($scope.promise).done(function(){
});
function whenAll -
function whenAll(promises) {
var i, data = [],
dfd = $.Deferred();
for (i = 0; i < promises.length; i++) {
promises[i].done(function(newData) {
// something
}).fail(function() {
//something
});
}
return dfd.promise();
}
I am getting the following error -
TypeError: promises[i].done is not a function
If you want to execute something when all your promises resolve...
...you should take a look at $q.all()
Enjoy!
This is not the angular way I should say you must use $q service for it.:-
$q.all([Your array]).then(function(values) {
//Your code
});
Service:-
this.fetch = function(index){
var url = some_url;
var def = $q.defer();
$http.get(url,{timeout:8000}).success(function(data) {
def.resolve(data);
})
.error(function() {
def.reject("Failed to get albums");
});
return def.promise;;
};
Controller:-
var promises = [];
var promise1 = geturl.fetch(0);
var promise2 = geturl.fetch(1);
promises.push(promise1);
promises.push(promise2);
$q.all(promises).then(function(results){
});
Hope it help :)

Angular: Rewriting function to use promise

I'm using an Angular factory that retrieves data from a feed and does some data manipulation on it.
I'd like to block my app from rendering the first view until this data preparation is done. My understanding is that I need to use promises for this, and then in a controller use .then to call functions that can be run as soon as the promise resolves.
From looking at examples I'm finding it very difficult to implement a promise in my factory. Specifically I'm not sure where to put the defers and resolves. Could anyone weigh in on what would be the best way to implement one?
Here is my working factory without promise:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $state, StorageHandler) {
var obj = {
InitData : function() {
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
},
};
return obj;
});
Here's what I tried from looking at examples:
angular.module('MyApp.DataHandler', []) // So Modular, much name
.factory('DataHandler', function ($rootScope, $q, $state, StorageHandler) {
var obj = {
InitData : function() {
var d = $q.defer(); // Set defer
StorageHandler.defaultConfig = {clientName:'test_feed'};
StorageHandler.prepData = function(data) {
var i = 0;
var maps = StorageHandler.dataMap;
i = data.line_up.length;
while(i--) {
// Do loads of string manipulations here
}
return data;
}
// Check for localdata
if(typeof StorageHandler.handle('localdata.favorites') == 'undefined') {
StorageHandler.handle('localdata.favorites',[]);
}
return d.promise; // Return promise
},
};
return obj;
});
But nothing is shown in console when I use this in my controller:
DataHandler.InitData()
.then(function () {
// Successful
console.log('success');
},
function () {
// failure
console.log('failure');
})
.then(function () {
// Like a Finally Clause
console.log('done');
});
Any thoughts?
Like Florian mentioned. Your asynchronous call is not obvious in the code you've shown.
Here is the gist of what you want:
angular.module("myApp",[]).factory("myFactory",function($http,$q){
return {
//$http.get returns a promise.
//which is latched onto and chained in the controller
initData: function(){
return $http.get("myurl").then(function(response){
var data = response.data;
//Do All your things...
return data;
},function(err){
//do stuff with the error..
return $q.reject(err);
//OR throw err;
//as mentioned below returning a new rejected promise is a slight anti-pattern,
//However, a practical use case could be that it would suppress logging,
//and allow specific throw/logging control where the service is implemented (controller)
});
}
}
}).controller("myCtrl",function(myFactory,$scope){
myFactory.initData().then(function(data){
$scope.myData = data;
},function(err){
//error loudly
$scope.error = err.message
})['finally'](function(){
//done.
});
});

Resources