Is there an AngularJS equivalent to $(document).ajaxSuccess()? - javascript

In AngularJS, I wish to create a catch-all-be-all ajax loader that does not need to be weaved into each controller in order to work. Traditionally, in jQuery I can do something like this:
(function globalAjaxLoader($){
"use strict";
var ajaxBoundElements = [$posts, $navigationLinks];
ajaxBoundElements.forEach(function($elm){
$elm.on('click', function(){
$loader.show();
});
$(document).ajaxSuccess(function (event, XMLHttpRequest, ajaxOptions){
$loader.hide();
});
})(jQuery);
However, in AngularJS I am not seeing a global way of detecting ajaxCompletion (that is without going through the promise returned for each ajax call made through Angular individually)?
Thanks.

Here I've put together a jsBin showing how to do this with an http interceptor.
I've used $rootScope.loadingCount so you can actually ng-show and ng-hide your based on that. Here is an example of the markup (you'd obviously use something a bit different:
<h1 ng-show="loadingCount > 0">Loading...</h1>
And here is the javascript:
angular
.module('app', [])
.config(httpInterceptorConfig)
.factory('loadingDialogInterceptor', loadingDialogInterceptor);
// create your interceptor
loadingDialogInterceptor.$inject = ['$q', '$rootScope'];
function loadingDialogInterceptor($q, $rootScope) {
$rootScope.loadingCount = 0;
function showLoading() {
$rootScope.loadingCount++;
}
function hideLoading() {
if ($rootScope.loadingCount > 0) {
$rootScope.loadingCount--;
}
}
return {
request: function (config) {
showLoading();
return config || $q.when(config);
},
response: function(response) {
hideLoading();
return response || $q.when(response);
},
responseError: function(response) {
hideLoading();
return $q.reject(response);
}
};
}
// actually register your interceptor
httpInterceptorConfig.$inject = ['$httpProvider'];
function httpInterceptorConfig($httpProvider) {
$httpProvider.interceptors.push('loadingDialogInterceptor');
}

You can achieve the same with interceptors.
myModule
.factory('httpResponseInterceptor', [
'$q',
function ($q) {
return {
request: function (request) {
$loader.show();
return request;
},
requestError: function () {
$loader.hide();
},
response: function (response) {
$loader.hide();
return response || $q.when(response);
},
responseError: function (rejection) {
$loader.hide();
return $q.reject(rejection);
}
}
}
])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('httpResponseInterceptor');
$httpProvider.defaults.transformRequest.push(function (data) {
$loader.show();
return data;
});
})

Related

How to know if all $http calls are over - AngularJS

I need to trigger an event when all of my $http calls are processed. I also need to know if any call has failed. I tried using the available solutions on stackoverflow such as using an interceptor.
angular.module('app').factory('httpInterceptor', ['$q', '$rootScope',
function ($q, $rootScope) {
var loadingCount = 0;
return {
request: function (config) {
if(++loadingCount === 1) {
$rootScope.$broadcast('loading:progress');
}
return config || $q.when(config);
},
response: function (response) {
if(--loadingCount === 0) {
$rootScope.$broadcast('loading:finish');
}
return response || $q.when(response);
},
responseError: function (response) {
if(--loadingCount === 0) {
$rootScope.$broadcast('loading:finish');
}
return $q.reject(response);
}
};
}
]).config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
However, with this approach the $rootScope.$broadcast('loading:finish') gets called after every $http call is completed. I want to trigger an event when all the $http calls are over.
I cannot use $q since the $http calls in my page belong to various directives and are not called in the same controller.
You can use below code to check the number of pending requests for $http. I am using this to show loading spinner in my project.
$http.pendingRequests.length
For keeping a track of failed and success calls you can use something like this:
angular.module('myApp', [])
.run(function ($rootScope){
$rootScope.failedCalls = 0;
$rootScope.successCalls = 0;
})
.controller('MyCtrl',
function($log, $scope, myService) {
$scope.getMyListing = function(employee) {
var promise =
myService.getEmployeeDetails('employees');
promise.then(
function(payload) {
$scope.listingData = payload.data;
$rootScope.successCalls++; //Counter for success calls
},
function(errorPayload) {
$log.error('failure loading employee details', errorPayload);
$rootScope.failedCalls++; //Counter for failed calls
});
};
})
.factory('myService', function($http) {
return {
getEmployeeDetails: function(id) {
return $http.get('/api/v1/employees/' + id);
}
}
});
Basically I have created 2 root scope variables and using it as counters to maintain count of success and failed calls which you can use wherever you want.

How to modify response (get) function in angular services?

I want to move this function to services.js:
News.all().async().then(function(data) {
$scope.news = data['data']['NewsList'];
});
And than call it in controller.js by this command:
$scope.news = News.all();
I try many ways, but them did not work.
Here is my services.js:
.factory('News', function($http) {
function returnNews() {
return {
async: function() {
return $http.get('test.json');
}
};
}
return {
all: function() {
return returnNews();
}
}
});
Well if you call News.all() in the end you will get an object with an async property: {async} and I don't think this is what you want. What you can do is pass a callback to the service:
.factory('News', function($http) {
return {
all: function(callback) {
return $http.get('test.json').then(callback);
}
}
});
and in the controller you have to do:
News.all(function(data){
$scope.news = data
});

How to pass the result of a promise to a controller in AngularJS

In a controller, I need to retrieve the status of a segment. The segments are loaded from an API using $resource.
In the resource, segmentsresource.js I have:
angular.module('appApp')
.factory('SegmentsResource', function ($resource) {
return $resource('http://localhost:3000/api/v1/segments/:id');
});
In the service, segmentsservice.js I have:
angular.module('appApp')
.service('SegmentsService', function (SegmentsResource) {
this.getSegmentStatus = function(segmentId) {
return SegmentsResource.get({
id: segmentId
}, function(segment) {
return segment.status;
})
};
});
I'm trying to do return segment.status so I can use the result ('available', 'translated', etc) in the controller, main.js:
$scope.checkAndEditSegment = function(segmentId) {
SegmentsService.getSegmentStatus(segmentId)
.then(function(status) {
if (status === 'available') {
console.log('segment is available');
}
});
};
However, this doesn't work. Console spits: TypeError: Cannot read property 'then' of undefined', so I have a problem with promises.
How to fix this?
But why you are taking this long route when you can simply call the factory from controller.
angular.module('appApp')
.factory('SegmentsResource', function ($resource) {
return $resource('http://localhost:3000/api/v1/segments/:id');
});
$scope.checkAndEditSegment = function(segmentId) {
SegmentsResource.get(segmentId)
.then(function(status) {
if (status === 'available') {
console.log('segment is available');
}
});
};
I resolved using $q:
In the service:
this.getSegmentStatus = function(segmentId) {
var dfd = $q.defer();
SegmentsResource.get({ id: segmentId }, function(segment) {
dfd.resolve(segment.status);
})
return dfd.promise;
};
In the controller:
SegmentsService.getSegmentStatus(segmentId)
.then(function(status) {
console.log(status);
});

Wait till $http is finished in order to output the result in AngularJS

How can I wait with my function till the $http request is finished?
My services.js looks as follows:
var app = angular.module('starter.services', []);
app.factory('Deals', function($http) {
function getDeals() {
$http.get('http://www.domain.com/library/fct.get_deals.php')
.success(function (data) {
var deals = data;
return deals;
})
.error(function(err){
});
}
return {
all: function() {
return getDeals();
},
get: function(keyID) {
//...
}
}
});
My controllers.js looks like:
var app = angular.module('starter.controllers', []);
app.controller('DealCtrl', function($scope, Deals) {
$scope.deals = Deals.all();
console.log($scope.deals);
});
The console.log in my controllers.js file outputs "undefined", but when I output the deals in the getDeals() function it contains the correct array which I get from my server.
What am I doing wrong?
$http and all of the async services in angularjs return a promise object. See promise api.
You need to use then method to assign it to a value in the scope.
So your controller:
app.controller('DealCtrl', function($scope, Deals) {
Deals.all().then(function (deals) {
$scope.deals = deals;
console.log($scope.deals);
});
});
Your service
app.factory('Deals', function($http) {
function getDeals() {
return $http.get('http://www.domain.com/library/fct.get_deals.php')
.success(function (data) {
var deals = data;
return deals;
});
}
return {
all: function() {
return getDeals();
},
get: function(keyID) {
//...
}
}
});

Global Ajax error handler with AngularJS

When my website was 100% jQuery, I used to do this:
$.ajaxSetup({
global: true,
error: function(xhr, status, err) {
if (xhr.status == 401) {
window.location = "./index.html";
}
}
});
to set a global handler for 401 errors. Now, I use angularjs with $resource and $http to do my (REST) requests to the server. Is there any way to similarly set a global error handler with angular?
I'm also building a website with angular and I came across this same obstacle for global 401 handling. I ended up using http interceptor when I came across this blog post. Maybe you'll find it as helpful as I did.
"Authentication in AngularJS (or similar) based application.", espeo software
EDIT: final solution
angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives'], function ($routeProvider, $locationProvider, $httpProvider) {
var interceptor = ['$rootScope', '$q', function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
window.location = "./index.html";
return;
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
Please note that responseInterceptors have been deprecated with Angular 1.1.4.
Below you can find an excerpt based on the official docs, showing the new way to implement interceptors.
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
return {
'response': function(response) {
// do something on success
return response || $q.when(response);
},
'responseError': function(rejection) {
// do something on error
if (canRecover(rejection)) {
return responseOrNewPromise;
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('myHttpInterceptor');
This is how it looks in my project using Coffeescript:
angular.module("globalErrors", ['appStateModule']).factory "myHttpInterceptor", ($q, $log, growl) ->
response: (response) ->
$log.debug "success with status #{response.status}"
response || $q.when response
responseError: (rejection) ->
$log.debug "error with status #{rejection.status} and data: #{rejection.data['message']}"
switch rejection.status
when 403
growl.addErrorMessage "You don't have the right to do this"
when 0
growl.addErrorMessage "No connection, internet is down?"
else
growl.addErrorMessage "#{rejection.data['message']}"
# do something on error
$q.reject rejection
.config ($provide, $httpProvider) ->
$httpProvider.interceptors.push('myHttpInterceptor')
Create the file <script type="text/javascript" src="../js/config/httpInterceptor.js" ></script> with this content:
(function(){
var httpInterceptor = function ($provide, $httpProvider) {
$provide.factory('httpInterceptor', function ($q) {
return {
response: function (response) {
return response || $q.when(response);
},
responseError: function (rejection) {
if(rejection.status === 401) {
// you are not autorized
}
return $q.reject(rejection);
}
};
});
$httpProvider.interceptors.push('httpInterceptor');
};
angular.module("myModule").config(httpInterceptor);
}());

Categories

Resources