AngularJS provide success callback of a factory method - javascript

I have a simple factory that returns a promise after a async.-request.
getUpdates: function(){
var q = $q.defer();
$http.get(...)
.success(function(data, status, headers, config) {
q.resolve(data);
})
.error(function(data, status, headers, config) {
q.reject(status);
});
return q.promise;
}
In my controller I just call the getUpdates-method and receive the promise.
var updates = factory.getUpdates();
How can I provide a success/error functionality of the getUpdates method?
factory.getUpdates().success(function(promise){...}).error(function(err)...
What do I have to add to my getUpdates function?

The $http service already return a promise, there is no need to use $q.defer() to create an another promise in this case. You could just write it like this:
getUpdates: function () {
return $http.get(...);
}
This way the success() and error() methods will still be available.
Or if you really have a reason why you are using $q.defer(), please include it in the question.

You then function to handle the success and failure of the promise:
factory.getUpdates()
.then(function(value) {
// handle success
}, function(reason) {
// handle failure
});

factory.getUpdates().then(
//onSucess
function(response)
{
// ...
},
//onError
function()
{
// ...
}
);

Related

Apply scope to promise .then() functions from service

I'm new to AngularJS.
I'm calling a service from a controller that is communicating with a webservice. the service returns a promise to the controller. Now I want to apply the data that is available on the success function of the promise to $scope. How can I achieve this? I already tried to do $scope.$apply() on the service call or the promise success function, but this just gives me errors.
Controller:
angular.module('home')
.controller('HomeCtrl',
['$scope', 'myService',
function ($scope, myService) {
$scope.data = [];
$scope.getData = function () {
// this is triggered via btn click
myService.getSomeData(reqData)
.then(
function success(res){
// apply res.data to $scope.data ?
},
function failure(err){
//error handling
}
);
}
}]);
Service:
angular.module('myService')
.factory('myService', ['$http', '$rootScope',
function ($http, $rootScope) {
return {
generalWebServiceProxy: function (webserviceName, data, xml) {
// do some xml stuff, settings header etc
return $http({
method: 'POST',
url: URL,
headers: headers,
data: data
});
},
getSomeData: function (data) {
return this.generalWebServiceProxy('WSName', data, true).then(/* do something here and return data*/);
}
}
}]);
Thanks in advance!
$http returns a promise where the server response is resolved. That response object contains things like status, data, header, etc.. etc...
Your service should handle these responses itself. So that the method setSomeData resolves to that data. Right now, it's resolving to the server response which isn't very useful for your directive.
generalWebServiceProxy: function (webserviceName, data, xml) {
// this method is return a promise
return $http({...});
},
// this method uses "promise chaining"
getSomeData: function (data) {
return this.generalWebServiceProxy(
'WSName',
data,
true).then(function(response) {
if(response.status == 200) {
return response.data;
}
throw new Error("Unexpected response:"+response.status);
});
}
In the above code I've done something called promise chaining. That's where the return value of a then mutates the resolve value. So the next promise then call will have response.data as the parameter.
Later in your directive you can use the promise returned by the service as a regular promise. It just resolves to the data in the response.
$scope.getData = function () {
myService.getSomeData(reqData)
.then(function success(data) {
// ^^ data is actually the value `response.data` from the service
});
}
What about error handling?
There are two kinds of errors with $http. There are HTTP errors like unable to resolve host, failure to connect or timeouts, and then there are response errors. Such as content missing or content not found. Some of these can be handled with the promise failure callback, and some are successful responses that you don't want.
So in my example I restrict success to mean an HTTP 200 response. You might want to check that the data given back is what you were expecting (for example, is it an array?).
I've found it better to handle errors using a HTTP interceptor. Otherwise you have to implement error handles in every directive that uses the service.
$http is a promise and over here you not returning the promise and using it as a promise in the controller.
Modified Service:
angular.module('myService')
.factory('myService', ['$http', '$rootScope',
function ($http, $rootScope) {
return {
generalWebServiceProxy: function (webserviceName, data, xml) {
// do some xml stuff, settings header etc
return $http({
method: 'POST',
url: URL,
headers: headers,
data: data
});
},
getSomeData: function (data) {
return this.generalWebServiceProxy('WSName', data, true);
}
}
}]);
And have the controller as is and update the $scope variable within success block.

Angular $scope not available outside my function

Somewhat new to Angular and javascript. I have the following controller written use a factory service to access a local JSON file. Most of this code is derived (or completely taken) from this post by Dan Wahlin. I am unable to access the $scope.books variable outside of the function and cannot figure out why. The console.log inside the function gives me the object I am looking for, but the one outside returns undefined. What am I doing wrong here? Thanks.
app.controller('FormController', ['$scope', 'tocFactory', function ($scope, tocFactory) {
$scope.books;
getBooks();
function getBooks() {
tocFactory.getBooks().
success(function(data, status, headers, config) {
$scope.books = data;
console.log($scope.books);
}).
error(function(data, status, headers, config) {
// log error
})
}
console.log($scope.books);
}]);
Because you are making an ajax and then executing the next line of code. Which doesn't mean you have guarantee that your ajax response is ready on execution of next line of code.
You could always gets its response value inside the success function of $http which gets called when ajax call executed successfully.
Read here How asynchronous call works?
actually this isn't an issue of scope, but timing. The getBooks function is executed asynchronously, so when your console log happens it most likely will not have been bound to anything. You could test this easily with interval to see how this is happening:
app.controller('FormController', ['$scope', 'tocFactory', function($scope, tocFactory) {
$scope.books;
getBooks();
function getBooks() {
tocFactory.getBooks()
.success(function(data, status, headers, config) {
$scope.books = data;
console.log($scope.books);
})
.error(function(data, status, headers, config) {
// log error
})
}
setInterval(function(){
console.log($scope.books);
}, 1000);
}]);
You can use $q service to handle asynchronous code with promises :
app.controller('FormController', ['$scope', '$q', 'tocFactory', function ($scope, $q, tocFactory)
{
var getBooks = function()
{
var deferred = $q.defer();
tocFactory.getBooks().
success( function(data, status, headers, config)
{
$scope.books = data;
deferred.resolve();
} ).
error( function(data, status, headers, config)
{
deferred.reject();
} );
return deferred.promise;
};
getBooks().then( function(res)
{
console.log($scope.books); // success : your data
}, function(res)
{
console.log($scope.books); // error : undefined
} );
console.log($scope.books); // undefined
} ] );
I haven't tested this code but it should work and show you promises principle.
More about $q service : https://docs.angularjs.org/api/ng/service/$q

How to return the soapresponse from interceptor's response

I am creating a SOAP request interceptor for AngularJS
It looks something like this:
angular.module('myApp')
.factory('SoapInterceptor', ['$q', function ($q) {
var soapRequest = function (url, SOAPAction, requestEnvelope, callback) {
$.soap({
url: url,
appendMethodToURL: false,
SOAPAction: SOAPAction,
enableLogging: false,
data: requestEnvelope,
success: function (SOAPResponse) { callback(SOAPResponse.toJSON()); },
error: function (SOAPResponse) { throw new Error(SOAPResponse); }
});
}
return {
'request': function (config) {
if (config.data && config.data.isSoap) {
var deferred = $q.defer();
soapRequest(config.url, config.data.soapaction, config.data.requestEnvelope, function (data) {
angular.extend(data, config);
deferred.resolve(data);
});
return deferred.promise;
}
return config;
},
'response': function (response) {
// I somehow want this returned response to be my soap response
// which i have got in request but of course it's no use there
return response;
}
}
}]);
So I can consume it inside a datastore's method like this:
var deferred = $q.defer();
$http.post("http://myapi.com/service.asmx",
{
isSoap: true,
requestEnvelope: reqXml,
soapaction: "http://myapi.com/CampaignsGetList"
})
.success(function (data) {
deferred.resolve(data);
});
return deferred.promise;
When isSoap is true the request correctly passed it to my soapRequest but how can I pass the response I get back to be returned by the response function so my consumer can happily use the promise?
Any help is appreciated.
If I understood this correctly what you are trying to do is to override the behaviour of the $http service when the data of the request content has the flag isSoap set to true. Looking at your code it seems that you actually want to handle the $http call yourself by using interceptors.
The problem is that interceptors are not meant to be used like that, what interceptors are supposed to do is handle things before and/or after the http request happens, but they are not supposed to handle the http request themselves.
However, I think that what you want is something like this:
Define your own "HttpSoap Service", like this:
app.service('HttpSoap', ['$q', function ($q) {
return function (url, SOAPAction, requestEnvelope) {
var deferred = $q.defer();
$.soap({
url: url,
appendMethodToURL: false,
SOAPAction: SOAPAction,
enableLogging: false,
data: requestEnvelope,
success: function (SOAPResponse) { deferred.resolve(SOAPResponse.toJSON()); },
error: function (SOAPResponse) { deferred.reject(SOAPResponse) }
});
return deferred.promise;
}
}]);
And use it like this:
app.controller('myController', function ($scope, HttpSoap) {
// other code here where I assume that you will define
// the reqXml variable
HttpSoap("http://proxy-send.concep.com/service.asmx", "http://new.cl.truelogic.com.au/CampaignsGetList", reqXml)
.then(function (jsonData) {
//things went well
}, function (errorResponse) {
//something bad happened
});
});
A couple other things that I would like to point out:
What you are doing in the second code snipped of your question is a "deferred anti-pattern" which is a very common bad practice that tends to happen among people who are starting to get familiar with promises. I used to fall for that all the time when I got started with Angular.
Notice that I got rid of the callback parameter that you had inside the function of your factory, since it's a bad idea to use callbacks in Angular, it's much better to use a $q promise instead.

how to use $q promises to work with multiple(in loop) asynchronous api call parallely in angular js?

I have an api :"http://me.apiary.com/1/user/{uid}", which i have to call multiple times parallely and asynchronously with various uid.how is it possible to call parallely and asychronously in angular js using promise($q) ? i have following code:
Angular Factory:
angular.module('witnessApp.Service')
.factory('File',['$http', function ($http) {
var baseUrl="http://me.apiary.com";
getFileById:function(uid){
return $http({
method: 'GET',
url: baseUrl+'/1/user/'+uid
});
}
}
return {
getFileById:fileTools.getFileById,
}
}]);
Angular Controller:
'use strict';
angular.module('witnessApp.Controller')
.controller('shareCtrl',['$scope','FileShare','$window','$routeParams','shareImgId','File', function ($scope,FileShare,$window,$routeParams,shareImgId,File) {
var id=$window.localStorage.id;
$scope.sharedImagesByID=[];
var uids=[1,2,3,4,5,6,7,8,9,0,11,10,12,13,14];
$scope.getFileById=function(){
for(var key in uids){
File.getFileById(uids[key])
.success(function(data, status, headers) {
$scope.sharedImagesByID.push(data.data)
})
.error(function(data, status, headers, config) {
console.error('error in loading File');
});
}
}
i tried using $q.all([]). but how i can handle loop using $q.all().i dont have any idea.please suggest me how to do this?
Is it possible to first 3 api call parallely.after that view should be render.and again 3 api call in background process. and so on till last api call. is it possible?
Try this:
$scope.getFileById = function() {
var promises = [];
for(var key in uids) {
var promise = File.getFileById(uids[key]);
promise.success(function(data, status, headers) {
$scope.sharedImagesByID.push(data.data);
});
promise.error(function(data, status, headers, config) {
console.error('error in loading File');
});
promises.push(promise);
}
$q.all(promises);
}
Define array, push all promises in to it, and call $q.all();

How can I replace $resource with $http in AngularJS?

My application has the following $resource calls. From what I can see this could be replaced with $http.
$resource('/api/:et/', { et: $scope.data.entityType })
.save(data, newSuccess, error)
.$promise.finally(last);
$resource('/api/:et/:id', { et: $scope.data.entityType })
.delete({ id: entityId }, deleteSuccess, error)
.$promise.finally(last);
$resource('/api/:et/:id', { et: $scope.data.entityType }, { update: { method: 'PUT' } })
.update({ id: entityId }, data, editSuccess, error)
.$promise.finally(last);
I have checked the $http documentation but I cannot see how to add the calls to the xxxSuccess, error functions and how to do the .$promise.finally(last).
Can someone explain how I could replicate this functionality using $http ?
$http is for general purpose AJAX. In most cases this is what you'll be using. With $http you're going to be making GET, POST, DELETE type calls manually and processing the objects they return on your own.
$resource wraps $http for use in RESTful web API scenarios.
Syntax
$http({
method : 'GET',
url : '/someUrl',
param : { paramKey : paramValue}, // optional
headers : 'someHeaders' // optional
}).success(function(data, status, headers, config)
{
// this callback will be called asynchronously
// when the response is available
}).error(function(data, status, headers, config)
{
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$http Documentation - https://docs.angularjs.org/#!/api/ng/service/$http
Maintaing Promise in $http
app.factory('myService', function($http) {
var myService = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('/someUrl').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
});
Take a look at this article Angular Promises, It will definitely benifit you in acheving such scenerios.
$resource is a further abstracted version of $http. If you are already using $response you may find it's not useful to change your logic to use $http. That said -
https://docs.angularjs.org/api/ng/service/$http
General usage
The $http service is a function which takes a single argument — a configuration object — that is used to generate an HTTP request and returns a promise with two $http specific methods: success and error.
$http({method: 'GET', url: '/someUrl'}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});

Categories