Overiding angularjs function $http.get - javascript

I have the following code:
$http.get(url).success(function(response,status,header,config) {
$scope.mymodel = response;
}
I want to check the http status and call a function.
I have to do this change on about 100 http.get calls in whole project. So I want to skip this overriding the success function like this.
function success(response,status,header,config) {
if(status!=200){
//my stuff
}
super.success(response,status,header,config);
}
Another possibility is replace $http.get(url).success for $http.get(url).success2 in order to tell my mates: "From now on guys use $http.get(url).success2 is cooler!"
Update
I have already know is a deprecated function, I must use it because is a project requirement.

You should use $httpProvider.interceptors to achieve this.
You can sniff the $http request and response between to do whatever you want and return the $q unscathed.
$httpProvider.interceptors.push(['$q', 'mathScriptLoadError', function($q, mathScriptLoadError) {
return {
requestError: function(rejection){
mathScriptLoadError.anyError(rejection);
return $q.reject(rejection);
},
responseError: function(rejection){
mathScriptLoadError.anyError(rejection);
return $q.reject(rejection);
}
};
}]);
In the above code I get the anyError function of the factory mathScriptLoadError to inspect the rejection and invoke some process based on values. But this doesn't disturb the $http.get at any level.

Deprecation Notice
The $http legacy promise methods success and error have been deprecated.
Use the standard then method instead.
If $httpProvider.useLegacyPromiseExtensions is set to
false then these methods will throw $http/legacy error.
-- AngularJS $http Service API Reference -- Deprecation Notice
Tell your mates: "From now on guys use $http.get(url).then is cooler!"
For other readers:
Response Interceptors
The AngularJS framework provides three places to inject response interceptor functions.
The $http service, see AngularJS $http Service API Reference -- interceptors.
The $resource service, see AngularJS $resource Service API Reference.
Configuring the $httpProvider, see AngularJS $httpProvider API -- interceptors.
XHR responses can be intercepted and modified globally, locally, or by class.

Related

How to mock $http.post method that has been called in other service method in jasmine?

I have and angular service that I want to test. In one of his methods I am using $http of angular service. I am simply want to mock that function (to be more specific mock $http.post function) that would return whatever I want and inject this mock to my service test.
I am tried to find solution and I found $httpBackend but I am not sure that this could help me.
MyService looks like that:
angular.module('app').service('MyService' , function (dependencies) {
let service = this;
service.methodToTest = function () {
$http.post('url').then(function () {
// Do something
});
}
}
I am want to test methodToTest and inject the mock of $http.post()
P.S please remember that $http.post() returns promise so I think that I need to consider in that.
this sounds like exactly what $httpBackend is for.
You might also be able to mock only $http.post by doing something like $http.post = jasmine.createSpy(); if you inject $http in your test, but I don't know.
If you do use $httpBackend, perhaps this example helps you, in your jasmine test do something like this
beforeEach(inject(function(_$httpBackend_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$httpBackend = _$httpBackend_;
$httpBackend.whenRoute('POST', 'url')
.respond(function(method, url, data, headers, params) {
expect(somevariable).toContain(params.something);
return [200, '{"progress":601}'];
});
}));
the $httpBackend will intercept all $http.post's to url and execute this function. It should be just like the methodToTest submitted to the actual url and got your fake return value.
The return value indicates a success http status code (200) and returns whatever you put in the second argument as the data property of the response (here response.data == '{"progress":601}'). Which will be in the then function. See How do I mock $http in AngularJS service Jasmine test?
The expect function there is just an example (not needed), to show you that you can put an expect clause there if you want.
P.S please remember that $http.post() returns promise so I think that I need to consider in that.
The service needs to return that promise:
angular.module('app').service('MyService' , function (dependencies) {
let service = this;
service.methodToTest = function () {
//$http.post('url').then(function () {
//vvvv RETURN http promise
return $http.post('url').then(function () {
// Do something
});
}
}
When a function omits a return statement, it a returns a value of undefined. There is no way that the service can indicate success or failure to the user.

Calling $http promise inside controller

Thanks in advance for the help...
I have a controller I'm using to call an API to send a password reset link. I have abstracted the actual $http call off into a service to keep the controller thin. Originally I was doing something like this:
angular.module('module')
.service('forgotPasswordService', ['$http', function($http) {
$http(request).then(function() {
return {}; //return some object using response
}]);
I felt like this would be the best approach as it would keep the controller as thin as possible and kept all service related actions separate. My problem with this was that returning from within the promise never actually returned me anything. I had to actually return the $http service call to get the promise. The only way I was able to make all of this work was if I called .then from within the controller.
//controller
angular.module('module')
.controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {
forgotPasswordService.forgotPassword(emailAddress).then(function() {
//do stuff
}
}]);
//service
angular.module('module')
.service('forgotPasswordService', ['$http', function($http){
this.forgotPassword = function(emailAddress) {
return $http(request);
};
}]);
This just feels a little wrong to me because the controller now depends on receiving a promise back from the service. I may just be overthinking this but I would like a second opinion.
Is this considered acceptable/good practice? Is there an alternative to this which would allow me to achieve the encapsulation I'm looking for?
Thanks again.
I've interfaced with the $http from the controller in two slightly different ways.
Like you it felt wrong returning the $http from the service and interfacing with it.
So first I created services and passed in a success method and an error method (callbacks).
// Service
angular.module('module')
.service('forgotPasswordService', ['$http', function($http) {
function sendForgotPasswordEmail(emailAddress, success, error){
$http.post('/api/v1/resetpassword', {emailAddress:emailAddress})
.then(success, error);
}
return {
sendForgotPasswordEmail: sendForgotPasswordEmail
}
}]);
// Controller
angular.module('module')
.controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {
forgotPasswordService.sendForgotPasswordEmail(emailAddress,
function(response){ //success
// notify user of success
},
function(response){ // error
// notify user of error
});
}]);
This worked great. I created an large application this way, but as I started on my second large angular project I wondered why I was hiding the $http's promise?
By passing back the promise, I can use other libraries that support promises. With my first approach I can't leverage other libraries promise support.
Passing back the $http promise
// Service
angular.module('module')
.service('forgotPasswordService', ['$http', function($http) {
function sendForgotPasswordEmail(emailAddress){
return $http.post('/api/v1/resetpassword', {emailAddress:emailAddress});
}
return {
sendForgotPasswordEmail: sendForgotPasswordEmail
}
}]);
// Controller
angular.module('module')
.controller('forgotPasswordCtrl', ['forgotPasswordService', function(forgotPasswordService) {
forgotPasswordService.sendForgotPasswordEmail(emailAddress)
.then(
function(response){ //success
// notify user of success
},
function(response){ // error
// notify user of error
});
}]);
I deleted my original answer, and I feel like a dork for stating that you could do it the other way. When I went back and checked my original code back when I first started angular, I found that I was calling then() twice in my application - once in my service where I returned the data, and once in my controller because calling $http(request).then() returns a promise.
The fact is, you're dealing with an asynchronous call. Suppose in your controller, you wanted to do this:
$scope.foo = myService.getFoo(); // No then()
The XmlHttpRequest inside the $http in getFoo() is an asynchronous call, meaning that it calls it and moves on. The synchronous option is deprecated. It's bad practice to make a blocking synchronous HTTP call because it will seize up your UI. This means you should use a callback when the data is ready, and the promise API is made just for that.
If you absolutely do not want to use the then() in your controller, I suppose you could probably pass your scope binding parameters to your service and let your service update them in your then call. I haven't tried this, and because it's asynchronous, I'm not sure if angular will know to call a digest() so you may need to call a $scope.$apply() if the values don't update. I don't like this, because I think the control of the values in the scope should be handled by the controller and not the service, but again - it's your personal preference.
Sorry for leading you astray with my initial answer - I ran into the same question you had, but when I looked back - I saw I used a silly solution.
-- Relevant statements in original answer --
Consider the following:
Where do you want your error handling for the call and who needs to know about it?
Do you need to handle specific failures in a particular controller or can they all be grouped together to one error handler? For some apps, I like to display the errors in a particular place rather than in a general modal dialog, but it's acceptable to create a service to handle all errors and pop them up for the user.
Where do you want to handle your progress/busy indicator?
If you have an interceptor wired up for all http calls and broadcasting an event to show/hide the busy indicator, then you don't need to worry about handling the promise in the controller. However some directives will use the promise to show a busy indicator which requires you to bind it to the scope in the controller.
To me, the decision is determined by the requirements and by personal choice.
Try using a callback like so:
angular.module('module')
.service('forgotPasswordService', ['$http', function($http) {
var recovery = function(request, callback) {
$http(request).then(function(response) {
callback(response);
})
}
return { recovery: recovery }
}]);
Then you would call it like this:
forgotPasswordService.recovery('http://...', function(response){
console.log(response);
})

AngularJS get dashboard settings using $htttp before initializing a dashboard controller

So I am still on a crash course with Angular. I am working on quite a complicated dashboard framework, all written in angular. Before I load the controllers, I need to get a bunch of dashboard settings from the server first using $HTTP. These settings are then used to control the layout of the dashboards.
So I read the way angular builds is by first running config methods, then run methods, then the controllers.
I can't use $HTTP in a config method, so I have built this in my main.js:
MetronicApp.run(['$rootScope','$http', function($rootScope,$http) {
var CUID = Cookies("CUID");
console.log('portlet settings for '+ CUID);
$http.get('/myurl/V3_portlet_settings?p_user_id='+CUID)
.then(function(response) {
console.log(response.data);
console.log('portlet status: ' + response.status);
$rootScope.$broadcast("dashSettings",response.data);
});
}]);
When I run, this all works happily and I see the data in the console.
Then in my controller:
$scope.$on( "dashSettings",
function(event,data){
$scope.dData = data;
console.log('dash data service identified in dash controller');
console.log($scope.dData.count);
} );
Couple of questions:
Is this the best way to get settings before initializing the dash. My plan would be to embed the calls that build the dash inside my $scope.$on block. I started looking at how to run a run method synchronously before the controllers initialize, but maybe I don't need to.
Any obvious idea why the $scope.$on method does not seem to fire?
Thanks in advance
A different approach would be to place your $http functions in a service or factory and then resolve these in your controller.
The key here is the use of promise. Angular documentation describes this as
A service that helps you run functions asynchronously, and use their
return values (or exceptions) when they are done processing
First create a service:
app.factory('DataService', function($http) {
var getValues= function() {
var url = '/myurl/V3_portlet_settings?p_user_id='+ CUID;
return $http.jsonp(url) // returns a promise
};
return {
getValues: getValues
}
});
And then in your controller:
myApp.controller('MyController', function ($scope, DataService) {
DataService.getValues().then( // resolve the promise using .then()
function(data){
// successcallback
// you can now safely populate the data in you controller
console.log(data);
},
function(error){
// errorcallback
console.log(error);
})
});
I think it is a better approach to use data services to handle data operations such as $http requests.
The return of promises allows for chaining of (multiple) promises and better handling of async calls.
You might find John Papa's style guide useful, especially the section about 'Separate Data Calls' (Y060) and 'Return a Promise from Data Calls' (Y061)

How do I fetch data from a Angular JS application using a company's own API?

I'm working on the Front End Development for a company with a CRUD application using Angular JS. They want me to build out the views and grab the data using their API. (I've never done this)
He's telling me the syntax to grab the data would be something like this(this is just an example):
var getItems = function () {
return $http
.get('/api/1/items/available')
.success(function (data, status, headers, config) {
$scope.items = data.items;
})
.error(function (data, status, headers, config) {
messageCenterService.add('danger', 'Unable to get the available Tickets and Passes.');
});
};
Then he said:
when you return the $http function, you can use it like:
getItems().then(... callback success, callback error...)
or move success() also
getItems().success(...).then(...)
Can someone help me understand what's going on here or guide me to a good tutorial with API's and Angular? I looked all over the web to find this and can't find anything that resembles this.
From the doc:
"
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.
...
Since the returned value of calling the $http function is a promise, you can also use the then method to register callbacks, and these callbacks will receive a single argument – an object representing the response.
"
$http.get is a shortcut method to perform GET request.
when you call $http.get('/api/1/items/available') you are making a GET request to '/api/1/items/available'.
As the request is asynchronous, you have to define a callback function to manipulate the result, and callbacks functions are registered by 'success' and 'then' methods of promise returned by $http call.
The 'error' method is used to register error callbacks.
If you are working with a REST api, you can use the Resource angular module.

How to write services with $q and $http calls without being repetitive

I'm trying to figure out an elegant way to write AngularJS services without being so repetitive with the $q syntax.
Currently, I'm writing services like follows:
(function() {
function ServiceFactory($q, $timeout, $http) {
return {
getFoo: function() {
var deferred = $q.defer();
$timeout(function() {
$http.get('/the/foo/thing').then(function(response) {
if (response.isError) {
deferred.reject();
} else {
deferred.resolve(response.data.foo);
}
}, function() {
deferred.reject();
});
});
return deferred.promise;
}
};
}
angular.module('myapp').service('MyService', ['$q', '$timeout', '$http', ServiceFactory]);
}.call(this));
It works very well, buy I'm always writing down a bunch of code just to delay $http.get and expose a Promise. Sometimes I will have some extra stuff into success callback, like handling data, creating a different response object... But most of the time, is just like the code above: call $q.defer + $http.get().then... + return promise
So, I'm thinking about a way to clean up/reduce the code without affecting the clarity of what I'm doing, e.g if another developer open the file, it should not be a mystery.
As a side note, the $http service here is actually a decorator, handling server responses to give me a more structured object with things like response.isError and response.data.
Also, I have seen a similar solution in another question {1}, but this is not as same. Just returning the response of $http.get().then() will expose the entire response to controllers on response, and it is not the desired effect. Instead, when I call MyService.getFoo().then(...), I'm expecting a foo object as a response from service, fed by server via response.data.foo, or a call to errorCallback.
I've forgot to mention: my server is not RESTful, so $resource is not an option right now. My URLs are more like /thing/get/:id, /thing/do-stuff/:id.
{1} Similar question
After thinking for a while, I figured out a better way to write the services. I've done some changes to $http response, and now my implementation is returning a Promise whenever a Controller calls http.get() or whatever http methods. With this implementation, I've reduced the methods' code to two or three lines per request in most of the cases. Now I should try to use the AngularJS decorator setup.
A working example is here:
http://embed.plnkr.co/p4EHQAbE40XWXjBWqjIM/preview

Categories