I've search for a sollution but I didn't find something like that.
I'm using angular, I want to call a function inside another function, and wait for its response.
the 2nd function is:
self.changeProvider = function() {
var contexec = false;
if (!checkIsFit()) {
contexec = true;
} else {
contexec = false;
}
if (contexec) {
var modalOptions = {
closeButtonText: $translate.instant('closeButtonText'),
actionButtonText: $translate.instant('ok'),
headerText: $translate.instant('changeProvidertitle'),
bodyTemplate: '../themes/default/src/app/shoppingCart/changeProvider/changeProvider.tpl.html',
margin: true
};
var modalDefaults = {
backdrop: 'static',
templateUrl: '../themes/default/src/app/shoppingCart/changeProvider/changeProvider.tpl.html',
controller: 'ChangeProviderCtrl',
size: 'sm',
resolve: {
modalData: function() {
return {
data: $scope.arrayToChangeProvider
};
}
}
};
modalService.showModal(modalDefaults, modalOptions)
.then(function(result) {
//some stuff
});
}
};
And the other function:
var checkIsFit = function() {
if ( $scope.cabstatus != 4 ) {
return false;
} else {
var modalOptions = {
closeButtonText: $translate.instant('closeButtonText'),
actionButtonText: $translate.instant('ok'),
headerText: $translate.instant('cabisfittedtitle'),
bodyTemplate: '../themes/default/src/app/shoppingCart/checkIsFit/checkIsFit.tpl.html',
margin: true
};
var modalDefaults = {
backdrop: 'static',
templateUrl: '../themes/default/src/app/shoppingCart/checkIsFit/checkIsFit.tpl.html',
controller: 'CheckIsFitCtrl',
size: 'sm',
resolve: {
modalData: function() {
return {
};
}
}
};
modalService.showModal(modalDefaults, modalOptions)
.then(function(result) {
if (result.msg === 'ok') {
var params = {
token: $scope.token,
fkidpedido: $scope.pendingOrderLineList[0].FK_IDPEDIDO,
userid : $scope.userid
};
shoppingCartService.postResetAgr(params, function() {
return true;
}, function() {
/*Notification.error({
message: $translate.instant('components.activity.actions.deleteActivityError')
});*/
});
return false;
} else {
return true;
}
});
}
};
The problem is the function changeProvider still executing and opens the modal first to resolve the funcion checkIsFit()
I want to wait checkIsFit is resolved and then continue with the functions of changeProvider
I cannot include the checkIsFit() functionallity inside changeProvider because I want to use checkIsFit() into another functions.
Any help will be appreciate.
Thanks in advance
I believe what you're looking for are deferred objects and promises. Check out the documentation for $q:
https://docs.angularjs.org/api/ng/service/$q
I'd recommend giving this a good read because this is a really important and powerful concept for ANY Javascript developer.
At the essence, deferred objects and promises allow you run asynchronous processes and callback to a function when a process is complete.
The modalService.showmodal method returns a promise. Create functions that return those promises.
var modalPromise1Fn = function () {
var promise1 =
modalService.showModal(modalDefaults1, modalOptions2)
.then(function(result) {
//some stuff
});
return promise1;
};
var modalPromise2Fn = function () {
var promise2 =
modalService.showModal(modalDefaults2, modalOptions2)
.then(function(result) {
//some stuff
});
return promise2;
};
This use the .then method of the first promise to chain the second promise.
var derivedPromise =
modalPromise1Fn().then( function() {
var promise2 = modalPromise2Fn();
//return to chain the second promise
return promise2;
});
From the Docs:
Chaining promises
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 .
-- AngularJS $q Service API Reference -- Chaining Promises
Related
I am using Angular resourse to get my data from an API, in this way:
var getAccountListPerUser = function () {
return $resource(uri, {}, {
get: {
headers: service.getDefaultHeaderRequest(),
method: 'GET',
transformResponse: function (data) {
var accountList = [];
try {
accountList = JSON.parse(data);
} catch (e) {
accountList = [];
}
return accountList;
},
isArray: true,
cache: true
}
}).get().$promise;
};
In my controller I have to use it and another two service functions defined in the same way.
var promiseResourcesAccountList = usrWebUserService.getAccountListPerUser();
promiseResourcesAccountList.then(function(result){
$scope.usersWithAccountsAndProfiles = result;
var filteredProfiles = [];
for (var account in result) {
...
}
$scope.filteredProfiles = filteredProfiles;
});
And:
var promiseResourcesEditUser = usrWebUserService.getResourcesUser(currentUser);
promiseResourcesEditUser.then(function (result) {
usrWebUserFactory.mapBasicPreferences($scope, result);
});
And then another very similar, this information loads data in three divs, but I want to show them only when all the three functions have completed correctly. I think I have to chain the result of the promises. How can I do that?
You can chain them like:
promiseResourcesAccountList.then(function(result){
///whatever processing
//return a promise
return promiseResourcesEditUser()
}).then(function(){
return anotherPromise();
}).then(function(){
//update scope here
});
alternatively, you could also use $q.all([promise1, promise2, promise3]).then(...);
#terpinmd is correct. Chaining promises is pretty simple. Say you have a service with a "getWidgets" that returns a promise, and you want to use the response from that service to call another service, "getWidgetOwners" that will return another promise :
Assumptions
getWidgets returns an array of widget objects.
getWidgetOwners accepts an array of ownerIds
How To:
service.getWidgets()
.then(function(widgets) {
return widgets.map(function(widget) { // extract ownerIds
return widget.ownerId;
});
})
.then(service.getWidgetOwners) // pass array of ownerId's to
.then(function(owners) { // the next service
console.log(owners);
});
I'm trying to make two API calls in parallel using $q.all, and return both of their responses as one to the controller, and when I break on the return lines for each promise inside the hash, they return the expected data, but it all seem to break when it reaches $q.all. This is all in a ui-router state, and I'm using resolve to supposedly provide the corresponding controller with the data from $q.all
It was originally written in Coffeescript, but here it is in Javascript:
resolve: {
content: [
'APIService', function($q, $timeout, APIService) {
var firstPromise, secondPromise, promises;
firstPromise = $q.defer();
secondPromise = $q.defer();
promises = {
firstPromise: APIService.get('/some/api/call').then(function(response) {
return response;
}),
secondPromise: APIService.get('/another/api/call').then(function(response) {
return response;
})
};
$.blockUI();
$timeout(function() {
firstPromise.resolve('firstPromise');
secondPromise.resolve('secondPromise');
}, 1000);
$q.all(promises).then(function(responses) {
$.unblockUI();
return responses;
});
return false;
}
]
}
Please help? I'm using Angular 1.3.15
content:[ should actually be :
resolve: {
content: function(){
// <create promise array>
return $q.all(promises);
}
}
It's hard to recreate your logic, but this example works well and hope will help (Angular 1.3)
angular.module('myApp',[])
.controller('MyCtrl', function ($scope, $q, $timeout) {
$scope.name = "Superhero"
var prom1 = $q.defer();
var prom2 = $q.defer();
var promises = {
prom1: prom1.promise,
prom2: prom2.promise
};
$timeout(function () {
prom1.resolve('prom1');
prom2.resolve('prom2');
}, 1000);
$q.all(promises).then(function (responces) {
$scope.prom1 = responces.prom1;
$scope.prom2 = responces.prom2;
});
});
http://jsfiddle.net/ugja9gth/1/
Example with real $http
http://jsfiddle.net/Evgeniy_D/ugja9gth/2/
I need to make submitAdapterAuthentication() function to work the first getUserRoles() function, but with the current implementation of the getUserRoles() function is being executed first that submitAdapterAuthentication(). How can I fix this?
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
}).then(function() {
submitAdapterAuthentication(user, pass);
}).then(function() {
getUserRoles();
});
function submitAdapterAuthentication(user, pass) {
var invocationData = {
parameters : [ user, pass ],
adapter : "adapterAuth",
procedure : "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
//return promise
WL.Client.updateUserInfo({
onSuccess : function() {
}
});
}
});
}
});
}
// my function to obtain roles
// It should be performed after submitAdapterAuthentication
function getUserRoles(){
var arrayRoles = [];
var attributes = WL.Client.getUserInfo(realm, "attributes");
if(attributes){
if(attributes.roles){
arrayRoles.push(attributes.roles);
}
}
}
When chaining promises, if you return anything but another promise from a then() callback, the resulting promise will be resolved immediately with the value undefined.
In order to make sure your callbacks are executed in the order you specified, just make sure each callback is returning a promise at the end. If you want to return some value from a callback, wrap it in a $q.when(). In this case it looks like you are not using any intermediary return values, so you can just wrap any arbitrary value in a $q.when() to make sure a promise is returned:
checkOnline().then(function(onl) {
userObj.isLoginOnline = onl;
return $q.when(true);
}).then(function() {
submitAdapterAuthentication(user, pass);
return $q.when(true);
}).then(function() {getUserRoles();});
Based on your latest edit, it looks like ch.submitAdapterAuthentication() is likely returning a promise. If this is the case, you should return this promise from the function:
return ch.submitAdapterAuthentication(invocationData, {...
And then return this promise in the then callback:
then(function() {return submitAdapterAuthentication(user, pass);})
If ch.submitAdapterAuthentication() does not return a $q promise, you will have to wrap it yourself:
var deferred = $q.defer();
ch.submitAdapterAuthentication(invocationData, {
onFailure : function(error) {
WL.Logger.log("ERROR ON FAIL: ", error);
deferred.reject(error);
},
onSuccess : function() {
WL.Client.updateUserInfo({
onSuccess : function() {
deferred.resolve();
}
});
}
});
return deferred.promise;
I am unable to wrap my brain around the concept of asynchronous requests.
I have a controller for my view, which is creating an object instance from a provider:
va.controller('VaCtrl',function($scope,$shipment){
$scope.shipment = $shipment.Shipment();
});
The provider:
Shipment.provider('$shipment',function(){
this.$get = function($http){
function Shipment(){
}
Shipment.prototype.fetchShipment = function(){
var shipment = undefined;
$http.post('../sys/core/fetchShipment.php',{
// some data to POST
}).then(function(promise){
shipment = promise.data;
});
return shipment;
};
return {
Shipment: function(){
return new Shipment();
}
}
}
});
My goal is to get access to the data from Shipment.prototype.fetchShipment() inside my controller. My approach:
$scope.fetchShipment = function(){
var shipment = $scope.shipment.fetchShipment();
console.log(shipment); // undefined
};
However, this will return undefined.
I read about $q, and defers, promises and callbacks, and now i am like WTF; all i want to do is to push the retrieved data to my controller, what is the best possible way to do so?
You should modify your code as shown below to return the promise from fetchshipment directly, and then use then() inside your controller.
Shipment.prototype.fetchShipment = function(){
return $http.post('../sys/core/fetchShipment.php',{
// some data to POST
})
};
$scope.fetchShipment = function(){
var shipment = $scope.shipment.fetchShipment().then(function(data){;
console.log(data);
});
};
Explanation to Code :
Calling $http return a promise which is resolved when you get the data from the server. In the code above, I have returned $http.post from service function which returns a promise. So in the controller you are waiting for promise to be resolved, and when the promise is resolved, the result is logged to the console.
Read about more promise documentation on angular:
http://docs.angularjs.org/api/ng.$q
http://docs.angularjs.org/api/ng.$http
Just the give you an example how to get your example working with your own promise.
It's much more simple if you use $http builtin promise, so it's an $q-example:
angular.module('myApp', []).controller("myAppCtrl", function ($scope, $shipment) {
$shipment.Shipment().fetchShipment().then(function (shipment) {
$scope.shipment = shipment
});
}).provider('$shipment', function () {
this.$get = function ($http, $q) {
function Shipment() {
}
Shipment.prototype.fetchShipment = function () {
var defered = $q.defer();
demodata = {name: "jan", id:8282};
$http.post('/echo/json/', 'json=' + encodeURIComponent(angular.toJson(demodata)), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).then(function (response) {
//resolve promise
defered.resolve(response.data);
});
return defered.promise;
};
return {
Shipment: function () {
return new Shipment();
}
}
}
});
<div ng-controller="myAppCtrl">{{shipment}}</div>
JSFiddle (use JSFiddle echo-service as data provider):
http://jsfiddle.net/alfrescian/ayke2/
More about promises:
http://blog.parse.com/2013/01/29/whats-so-great-about-javascript-promises/
http://www.egghead.io/video/o84ryzNp36Q
AngularJS : Where to use promises?
stackoverflow.com/questions/15604196/… egghead.io/video/o84ryzNp36Q
I'm having some trouble with getting a simple async test working. The following piece of code doesn't throw any errors in the console, even though it should, because the data passed to the function does not equal 0:
define([
'intern!bdd',
'intern/chai!expect'
], function (bdd, expect) {
with (bdd) {
describe('Test', function () {
it('async test', function(){
var dfd = this.async(2000);
var wait = function(ms) {
setTimeout(function(){
dfd.resolve('test');
}, ms);
return dfd.promise;
};
wait(1500).then(dfd.callback(function (data) {
// data === 'test', so this should fail.
expect(data).to.equal(0);
}), dfd.reject.bind(dfd));
});
});
}
});
I'm pretty sure I messed up somewhere because I never worked with promises until now, but I can't figure out where. Any ideas would help a lot. Thanks!
You’re using the same Deferred object for two different asynchronous operations and resolving it (= successful test) the first time. You need to create your own separate Deferred object for the wait function:
define([
'intern!bdd',
'intern/chai!expect',
'intern/node_modules/dojo/Deferred'
], function (bdd, expect, Deferred) {
with (bdd) {
describe('Test', function () {
it('async test', function(){
var dfd = this.async(2000);
var wait = function(ms) {
var waitDfd = new Deferred();
setTimeout(function(){
waitDfd.resolve('test');
}, ms);
return waitDfd.promise;
};
wait(1500).then(dfd.callback(function (data) {
// data === 'test', so this should fail.
expect(data).to.equal(0);
}), dfd.reject.bind(dfd));
});
});
}
});