Is there any way of adding a callback method to a function that calls service
in a function i am calling multiple service methods and i want to a set a callback method to that function.
I tried this but no luck
$scope.MyFunction() = function()
{
//Multiple Service calls
$scope.callService1 = service.CallService1() //returns true on success
$scope.callService2 = service.CallService2() //returns true on success
$scope.callService3= service.CallService3() //returns true on success
if($scope.callService1 && $scope.callService2 && $scope.callService3)
{
$scope.CallbackMethod ();
}
}
$scope.CallbackMethod = function()
{
alert('CallbackMethod')
}
I tried this one too but its not in sync as the service calls takes some time.
$scope.MyFunction() = function(CallbackMethod)
{
//Refer Above Code
}
CallService service Method is something simple like
$http.post('/InstStrategy/ReadAll').then(function (response) {
return true
});
Assuming that CallService1, CallService2 and CallService3 are async methods, they should return a deferred promise. For example:
this.CallService1 = function() {
// Once the result is available resolve the promise.
return $http.post('/InstStrategy/ReadAll').then(function(response) {
return true;
});
}
Now, it's time to define your function:
$scope.MyFunction = function(callback) {
var callService1 = service.CallService1(),
callService2 = service.CallService2(),
callService3 = service.CallService3();
// We want to wait for all these three methods to complete.
$q.all([callService1, callService2, callService3])
.then(function(results)) {
// Results is an array containing the results of each of your service calls.
var allTrue = true;
angular.forEach(results, function(result) {
if (!result) allTrue = false;
});
// If all the service calls where true, perform our callback.
if (allTrue) callback();
});
};
Note that the allTrue check is not really necessary, since the promises are systematically resolved with true.
Related
I have one Angular.js method from child controller, where it makes a call to twop parent controller methods one after another. But the second function should get the account data from from the first function and then will update call data like below:
filter.filterAccountsByProductMetrics1 = function(productWithSegmentations12) {
accountService.fetchAccountForRecordType([filter.selectedHcpHco.Name.display])
.then(function(resp) {
$scope.accountDataUpdate({
accounts: resp.data
});
var productId = null;
if(filter.selectedMySetupProduct.Product_vod__c) {
productId = filter.selectedMySetupProduct.Product_vod__c.value;
}
callService.getCallsForProductId(productId)
.then(function(calls) {
filter.filterRecords[filterType.product.value] = calls;
$scope.callDataUpdate({
calls: applyAllFilterOnCalls()
});
});
});
};
I've checked both the functions are getting called but the sequence is not maintained. How to make sure the two parent functions get called one after another.
EDIT: function accountDataUpdate:
call.accountDataUpdate = function(accounts) {
call.accounts = accounts;
getCallDetails(getCallIdsFromCallsForFilteredAccount())
.then(function() {
updateProductFrequencyTableData();
updateAccountDetailData(true);
});
updateDailyFrequencyChartData();
updateWeeklyFrequencyChartData();
updateCallFrequencyTableData();
updateAccountFrequencyData();
$timeout(function() {
$scope.$broadcast('updateDoughnutChart');
$scope.$broadcast('updateBarChart');
});
};
Modify accountDataUpdate to return a promise:
call.accountDataUpdate = function(accounts) {
call.accounts = accounts;
var promise = getCallDetails(getCallIdsFromCallsForFilteredAccount())
.then(function() {
updateProductFrequencyTableData();
updateAccountDetailData(true);
updateDailyFrequencyChartData();
updateWeeklyFrequencyChartData();
updateCallFrequencyTableData();
updateAccountFrequencyData();
return $timeout(function() {
$scope.$broadcast('updateDoughnutChart');
$scope.$broadcast('updateBarChart');
});
});
return promise;
};
Then use that promise for chaining:
filter.filterAccountsByProductMetrics1 = function(productWithSegmentations12) {
return accountService.fetchAccountForRecordType([filter.selectedHcpHco.Name.display])
.then(function(resp) {
return $scope.accountDataUpdate({
accounts: resp.data
});
}).then(function() {
var productId = null;
if(filter.selectedMySetupProduct.Product_vod__c) {
productId = filter.selectedMySetupProduct.Product_vod__c.value;
}
return callService.getCallsForProductId(productId)
}).then(function(calls) {
filter.filterRecords[filterType.product.value] = calls;
return $scope.callDataUpdate({
calls: applyAllFilterOnCalls()
});
});
};
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.
It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
For more information, see AngularJS $q Service API Reference - Chaining promises.
I have one function, I am required to print the lastlockerbalance outside the function.
var getlockerbalance = function() {
$http.get("php/selectLOCKERBALANCE.php").success(function(lockerbalance) {
$scope.lockerbalance1 = lockerbalance;
var lastlockerbalance = $scope.lockerbalance1[0].LOCKERBALANCE;
console.log(lastlockerbalance);
});
};
console.log(lastlockerbalance);
Showing error message as "lastlockerbalance is undefined".
You can't. The .success() callback won't even have been called by the time your second console.log() function is called, in fact the request $http.get() call won't even send the request until after the getlockerbalance() function returns.
What you need to do is move the code that uses the value inside the callback.
You could either return a promise and use then() or pass a callback to the function.
Promise
function getlockerbalance() {
return $http.get("php/selectLOCKERBALANCE.php").success(function(lockerbalance) {
$scope.lockerbalance1 = lockerbalance;
var lastlockerbalance = $scope.lockerbalance1[0].LOCKERBALANCE;
return lastlockerbalance;
});
};
getlockerbalance().then(function(result){
console.log(result);
});
Callback
var getlockerbalance = function(callback) {
$http.get("php/selectLOCKERBALANCE.php").success(function(lockerbalance) {
$scope.lockerbalance1 = lockerbalance;
var lastlockerbalance = $scope.lockerbalance1[0].LOCKERBALANCE;
callback(lastlockerbalance);
});
};
function printBalance(balance) {
console.log(balance);
}
getlockerbalance(printBalance);
I have about 5 requests that are made when my view loads. This is for an edit form:
var reqA = function() {...};
var reqB = function() {...};
var reqC = function() {...};
var reqD = function() {...};
var reqE = function() {...};
Now I want reqA() and reqB() to load asynchronously and if possible return a single promise.
reqC() and reqD() should only load after reqA() and reqB() have finished and executed their promise.
reqE() should only load after reqC() and reqD().
This is what I want to do, however I have no idea how to go about it. I can load them all asynchronously or one after another by chaining promises but not in the way I want.
If your functions all use the $http provider, it is easy to accomplish. You will want to modify your functions to return the result of the $http call like this
function reqA() {
return $http.get(...);
}
Now that they are returning promises, you can easily use the $q provider to orchestrate your requests:
$q.all([reqA(), reqB()])
.then( () =>
$q.all([reqC(), reqD()])
.then(() => reqE())
);
For regular JS (non ES6):
$q.all([reqA(), reqB()])
.then(function() {
$q.all([reqC, reqD()])
.then(function() {
reqE();
});
});
If you don't want to return the result of $http in your functions, you will need to set them up to return a promise in one way or the other:
function reqA() {
var deferred = $q.defer();
... your request code ...
// call this if everything is ok
deferred.resolve();
// call this if there was an error
deferred.reject();
return deferred.promise;
}
Here is an example that you can take a look at that might make it clearer. It makes use of Promise which is globally available in almost all javascript environments (excluding IE and older node versions).
angular
.module('example', [])
.run(function($q) {
function requestOne() {
return $q.when("one")
}
function requestTwo() {
return $q.when("two")
}
function requestThree() {
return $q.when("three")
}
function requestFour() {
return $q.when("four")
}
function requestFive() {
return $q.when("five")
}
$q
.all([requestOne(), requestTwo()])
.then(function(responses){
console.log(responses[0] === "one");
console.log(responses[1] === "two");
return $q.all([requestThree(), requestFour()]);
})
.then(function(nextResponses){
console.log(nextResponses[0] === "three");
console.log(nextResponses[1] === "four")
return requestFive();
})
.then(function(lastResponse){
console.log(lastResponse === "five")
})
});
angular.element(document).ready(function() {
angular.bootstrap(document, [ 'example' ]);
});
I assume that you are using angular 1, if you aren't there Promise is available in the global scope in almost all environments.
I have a series of services that either fetch data from an API server, or return data if it exists in local storage.
.factory('LogEntryResource', function(config, $resource) {
return $resource(config.apiUrl + 'logentries/:id/');
})
.factory('LogEntry', function(localStorageService, LogEntryResource) {
var localLogEntries = localStorageService.get("logEntries");
return {
all: function() {
if(localLogEntries){
return localStorageService.get("logEntries");
} else {
return LogEntry.query(function(data){
localStorageService.set("logEntries", data);
return data;
});
}
},
get: function(logEntryId){
...
},
delete: function(logEntryId){
...
},
update: function(logEntryId){
...
}
}
})
The problem is that in the app controllers sometimes a promise is returned, and sometimes the data is returned, so I need to handle the return value of LogEntry.all() to either wait for the promise to resolve or to use the data.
I'm not really sure how to go about it because I can either use a .then() which works for the promise, but is undefined if it has data, or vice-versa. I know I'm doing something wrong and looking for advice how to handle this situation of dealing with either data or a promise being returned.
.controller('LogEntryCtrl', function($scope, LogEntry) {
// var data = LogEntry.all();
// var promise = LogEntry.all();
$scope.logEntry = ???
}
I'm hoping there's a nice reusable solution instead of having to do a check to see what it is every time I use this code in my controllers/routes
// trying to avoid doing this
var logEntry = LogEntry.all();
if(logEntry.isPromise){
// do promise stuff here
} else if(logEntry.isData {
// do data stuff here
}
My suggestion would be always return a promise. You can use $q.resolve() to create a shortcut for a resolved promise
.factory('LogEntry', function(localStorageService, LogEntry, $q) {
var localLogEntries = localStorageService.get("logEntries");
return {
all: function() {
if(localLogEntries){
return $q.resolve(localLogEntries);
} else {
return LogEntry.query(function(data){
localStorageService.set("logEntries", data);
// update variable also
localLogEntries = data;
return localLogEntries ;
});
}
},
In controller you always use then() this way
LogEntry.all().then(function(data){
$scope.data = data;
});
I have some list of functions to call in controller say
$scope.trader = {};
$scope.getUserLocation();
$scope.searchPaymentMethod($scope.trader.cityname);
$scope.getUserCurrency();
and each of these functions make an HTTP call to get some data. Say getUserLocation does this...
$scope.getUserLocation = function() {
var dataPromise = locationServiceCustomised.getCurrentLocation();
dataPromise.then(function(result) {
$scope.trader.cityname=result.countryName;
});
}
and these all functions set some value to $scope.trader whose value is being used in calling another function.
Now how to make one by one Asynchronous call to each of these functions so that these functions work one after another.
My full code is somewhat like this...
$scope.trader = {};
$scope.getOfferList = function() {
if($scope.trader.cityname == null || $scope.trader.cityname == '') {
$scope.getUserLocation();
}
$scope.searchPaymentMethod($scope.trader.cityname);
$scope.getUserCurrency();
}
$scope.getUserLocation = function() {
var dataPromise = locationServiceCustomised.getCurrentLocation();
dataPromise.then(function(result) {
$scope.trader.cityname=result.countryName;
});
}
$scope.searchPaymentMethod = function(country) {
locationService.paymentMethod({cityname: country}, function(data) {
------- Some Functionality-------
});
};
$scope.getOfferList();
If you have asynchronous functions that depend on values obtained from previous asynchronous functions, you need to chain promises with .then. If the functions can be called in parallel, then you need to use $q.all.
In your case, searchPaymentMethod is strictly dependent on value from getLocation, while getUserCurrency can be done in parallel.
$scope.getOfferList = function() {
var paymentMethodPromise =
$q.when($scope.trader.cityname)
.then(function(cachedLocation){
if (cachedLocation) return cachedLocation;
return getUserLocation();
})
.then(function(location){
// cache, if needed, but better do this in a service
$scope.trader.cityname = location;
return searchPaymentMethod(location);
});
var currencyPromise = getUserCurrency();
// when all complete, calculate offerList
return $q.all({
paymentMethod: paymentMethodPromise,
currency: currencyPromise })
.then(function(data){
var paymentMethod = data.paymentMethod,
currency = data.currency;
// based on that get offerList
return offersList;
})
};
Needless to say, that for this to work, the expectation is for all these functions (i.e. searchPaymentMethod, getUserLocation, etc...) return a promise of the right value. As an example, getUserLocation doesn't do that now, and should be changed to something like the following:
function getUserLocation(){
return locationServiceCustomised
.getCurrentLocation()
.then(function(location){
// manipulate the result if needed
return location.cityName;
})
}
A few other things to note:
You don't need to expose every function on the $scope - expose only the ones that need to be invoked from the View, e.g. <button ng-click="getOfferList()">
It's better to leave the cached-or-not decision to the service (that should implement these functions) and leave a clean API for the controller.
You basically need to chain promises. So, first of all, all your functions have to return a promise. So, for your first function you would have:
$scope.getUserLocation = function() {
var deferred = $q.defer();
locationServiceCustomised.getCurrentLocation.then(function(result) {
deferred.resolve(result);
});
return deferred;
}
Then just chain it:
$scope.getUserLocation()
.then($scope.searchPaymentMethod)
.then($scope.getUserCurrency);
where
$scope.searchPaymentMethod = function(input) {
var deferred = $q.defer();
$scope.trader.cityname = input.cityname;
// do some computationn
deferred.resolve(whateveryouwanttoreturn);
returnn deferred;
}