Does angularJS modelizer fetch provide a way to specify an onSuccess handler? - angularjs

Is there a way to specify an "onSuccess" handler when using Angular-modelizer fetch? I looked at the angular-modelizer-0.2.24 source implementation of "fetch"
but the Javascript in there is boggling my mind... Does anyone have sample code?
I would prefer not to use a $future or "promise"; I would like to treat the fetch as synchronous.

After some further research I figured this out; the first callback function is the "success" path, and the second callback is the error path. Code sample included.
Given that the underlying AJAX call is asynchronous, I believe that the prevalent current pattern is to use $q and $promise, so that is what I have done.
var deferred = $q.defer();
var retModel = undefined;
//modelizer fetch
myModelObject.fetch(config).then(
//1st function is the "success" function
function(response) {
console.log ("Fetch success path");
retModel = response;
deferred.resolve(retModel);
},
//2nd function is the error function.
function(errResponse) {
console.log ("Error on fetch");
deferred.reject(errResponse);
//ErrorService.buildErrors(errResponse);
}
);
return deferred.promise;

Related

AngularJS: Chaining $timeouts and Promises

I am struggling with chaining promises using $timeouts. I would like to have a "$timeout(myFunction,1000).then()" function that fires only when ALL chained timeouts returned by myFunction are resolved.
This code snippet contains different stuff I tried and I would like to achieve:
$timeout(myFunction,1000).then(function(myPromise) {
console.log("I would like this message to appear when ALL chained promises are resolved, without knowing in advance how many chained promises there are. In this case this would be after 1500 ms, not 1000ms")
myPromise.then(function()) {
console.log("with this code patern I get a message after 1500ms, which is what I want, but this does not work anymore if myOtherFunction would return a third chained $timeout")
}
})
myFunction = function() {
console.log("hi, i am launching another timeout")
return $timeout(myOtherFunction, 500)
}
myOtherFunction = function () {
console.log("1500 ms have passed")
}
How should I fix my code? Thanks!
Return promises to the success handler:
$timeout(null,1000).then(function() {
console.log("It is 1000ms");
var delay = 500
return myPromise(delay);
// ^^^^^^ return promise for chaining
}).then(function() {
console.log("this happens after myPromise resolves");
});
function myPromise(delay) {
promise = $timeout(null, delay);
return promise;
});
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;
Inspired by the answer of georgeawg I created my custom timeout function that returns the promise returned by fct, instead of the promise returned by $timeout. I did this to keep the $timeout syntax.
vm.customTimeout = function (fct, timeout){
return $timeout(fct, timeout).then(function(myReturnedPromise){
return myReturnedPromise
});
}
This function is sufficient to solve my problem above. I can chain as much customTimeouts I want.
Example :
vm.customTimeout(myFunction,1000).then(function() {
var activity1 = anyFunctionReturningAPromise(100);
var activity2 = anyFunctionReturningAPromise(1000);
return $q.all([activity1, activity2])
console.log("Without knowing the content of myFunction, I am 100% sure that
every single chained promise retuned by myFunction is resolved before
executing this code, which is quite nice!")
}).then(function(){
console.log("executes when customTimeout, activity1 & activity2 are all resolved.")
})
anyFunctionReturningAPromise = function(delay) {
return vm.customTimeout(myFunction, delay)
}
Feel free to comment what you think of it.
I hope this will be useful for someone else :)

When do $q promises resolve

I have a following piece of code:
function getData(foos, bars) {
var generated = {};
return $q(function(resolve, reject) {
var promises = _.map(foos, function(foo) {
return _.map(bars, function(bar) {
return someServiceCall(foo, bar).then(function(data) {
_.set(generated[foo.id], player.id.toString() + '.data', data);
});
});
});
// Join all promises in to a final resolve
$q.all(promises).then(function() {
resolve(generated);
}, reject);
});
}
What I want to achieve is to have all the someServiceCall-s and it's success handlers finished by the time resolve(generated) is called. When I debug this part of the code in the developer toolbar, resolve(generated) is called before the success handler of someServiceCall is called for every promise.
Note: This not always breaks the functionality, as the objects are passed as a reference, so the data is set even if resolve was already called, but I think the functionality would be clearer if all those calls were finished by the time resolve is called.
I just realized my dumb mistake. The two nested maps resulted the following structure:
var promises = [[Promise, Promise][Promise, Promise, Promise]];
While $q.all expects an array of promises:
var promises = [Promise, Promise, Promise, Promise, Promise];
I could easily solve this by replacing the first map call by flatMap:
return _.flatMap(bars, function(bar) {
It's still strange for me that $q.all silently just resolved the promise without an error or warning that the data format is not appropriate.
I hope I can help someone who runs into this problem in the future.

AngularJS + Jasmine + handle of promises

Im using angularjs 1.4 and jasmine 2.4.
Im trying to test a function and I want to make it return a promise, so another layer above it can deal possible values.
My issue is that the function first validate the inputs. If they are not the right ones I want to return a rejected promise. Otherwise it will do whatever it has to do and resolve the promise.
Here is part of the function from the emailSvc in question:
// Function found in the emailSvc
this.sendEmail = function sendEmail(apiKey, token, email_type)
{
// Prerequisite to send email
if(!apiKey) {
return $q.when("apiKey not present.");
}
var deferred = $q.defer();
// Ajax call
serviceApiEmail.send(apiKey, token, email_type)
.then(function(data){
deferred.resolve(data);
})
.catch(function(e){
deferred.reject(e);
})
return deferred.promise;
}
And my test case is like follow:
it('should reject sending email if apiKey is not present', function(){
var rejectEmail;
var apiKey,
verifyToken = acceptedVerifyToken,
emailType = const_EMAIL_TYPE.SIGNUP;
i_emailSvc.sendEmail(apiKey, verifyToken, emailType)
.then(function(){
// It should not come here
rejectEmail = false;
}).catch(function(){
rejectEmail = true;
});
// It comes here without executing any success or fail promise handlers
expect(rejectEmail).toBe(true);
});
The issue is that when rejecting the promise the catch is never executed. I believe this is with a misconception I have with promises.
Any ideas why the catch and then are not working in here?
You are resolving the promise if the apiKey is not present when using $q.when(). This would indicate a successfully completed promise. To indicate failure you should use $q.reject i.e.
if(!apiKey) {
return $q.reject("apiKey not present.");
}
Additionally for the then or catch callbacks to be executed in your test you would usually need to trigger a digest cycle. The usual way of doing this is tests is to get a reference to the $rootScope and call $rootScope.$digest().
Are you mocking the serviceApiEmail.send method? Are you reaching the then at all? I'd check for something else then rejectEmail boolean because with Javascript your test is going to be false even if the 'then' part is not reached, so you aren't maybe even reaching the promise resolve part

Order of execution of $http callbacks

It seems that factory methods execution priority is the highest, so that callbacks has no data to deal with. What is the best way to make this work?
I got this kind of factory
app.factory('jsonService', function($http) {
return {
getDistricts: function(callback) {
$http.get('data/districts.json').success(callback);
},
getLocations: function(path,callback) {
$http.get('data/'+path+'.json').success(callback);
}
};
});
And controller
var app = angular.module('sandbox', []);
app.controller('sandboxCtrl',function ($scope,jsonService) {
//This one works
$scope.init1= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
})
});
};
$scope.init1();
//This one does not
$scope.init2= function(){
jsonService.getDistricts(function(data){
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
})
jsonService.getLocations($scope.currentDistrict,function(data){
$scope.locations1 = data;
});
};
$scope.init2();
});
Here is working plunker
Angular has an implementation of promises named $q (documentation) that you should read up upon.
There is a race condition due to the async nature of http calls. Please review the updated code linked to below that shows an example of your code running (successfully) using promises to handle your two calls in succession.
So upon success of your first call it will call your second service method all without using callbacks thanks to the power of promises.
jsonService.getDistricts()
.success(function(data) {
$scope.districts = data;
$scope.currentDistrict = $scope.districts[0].name;
jsonService.getLocations($scope.currentDistrict)
.success(function(locationData) {
$scope.locations = locationData;
})
});
updated PLKR
Promise clarification:
The raw implementation of basic promises uses then to handle responses and promises returned from $http add additional methods (success, error) that will unpack your data from the response object that you would need to handle if your just use then.
init1() is the correct way of doing this. init2() does work because jsonService.getLocations() is getting invoked before jsonService.getDistritcs() completes. The angular $http service is asynchronous. Since jsonService.getLocations() depends on data from jsonServicd.getDistricts() you must wait until .getDistricts() completes before calling .getLocations(). One way to do that is to call .getLocations() within the .getDitricts() callback, just as you did in init1().

angularJS $http request 'how to sync'

I think you find this question thousands of times...but I can't really understand the way to solve.
I have a $http request inside a Service...
app.service('getData', function ($http) {
this.getDataList = function () {
$http.get('../content/catalog/data.json')
.success(function(response) {
return response;
})
};
I call it from the app.run
app.run(function (getData) {
list=getData.getDataList()
})
If I log the list variable is undefined
What is the way to sync them?
Thank you for the help!!!!
You're treating the call as if it were synchronous, where as its an Async call, so when you say return response; that line executes when the call hasn't finished yet and naturally you get undefined, use a callback instead or return a promise:
this.getDataList = function (callback) {
$http.get('../content/catalog/data.json')
.success(function(response) {
callback(response);
})
};
Usage:
getData.getDataList(function(data){
var list=data;
console.log(data);
});
EDIT:
Regarding promises, the idea is very simple, whenever you have an async operation that you expect will not be completed immediately, i.e. ajax calls, you can use a promise returned by the method making that async operation in order to find out when the task has finished.
For example $http returns a promise by default, so you can change your code to make use of that promise by simply returning the $http call itself:
this.getDataList = function () {
return $http.get('../content/catalog/data.json');
}
and then use it like this:
getDataList().then(function(successData){
var list=successData;
},function(errorResponse){
alert("something terrible has happened!");
})
The promise returned by $http takes 2 callbacks, the first for a successful call and the second is for errors.
Nowadays I mostly just pass callbacks in to the function making the async call, saves me from having to write then().
list is undefined because you forgot to return $http.get in this.getDataList()
By the way, $http will return a promise (an object with then and finally methods), which you can directly use in ng-bind for instance.
Use promises instead.
JavaScript uses promises for async(deferred) operations. These promises are based on callbacks(I promise to run your callback when I'm done).
getDataList does a http.get behind the scenes and doesn't block the code. It simply returns a promise object.
You can add callbacks to the promise object that will happen when the async operation finishes.
app.service('getData', function ($http) {
this.getDataList = function () {
return $http.get('../content/catalog/data.json');
};
});
app.run(function (getData) {
getData.getDataList().then(function(res){
list = res
});
});

Resources