12

I followed this post to implement a similar ajax loader image on a project:

My implementation has a few differences:

  • I use $rootScope to emit and not broadcast and I also use $rootScope on the directive to handle the event.
  • Because of a particularity of the project, I have to unbind the directive $rootScope.$on listeners right after the first event being fired (either for show or hide), inside the event handler.
  • I only fire a single show/hide event. Show on the first HTTP request, hide when the count reaches 0.

I believe those are the major differences from the linked post, not sure if they are relevant to my question but just in case they are...

When the loader hide event is handled, the loader is gone and I won't show it again unless the page is refreshed, but I still have background http requests to refresh data on the current page. Those requests will still be intercepted and fire new show/hide events, which are no longer required/handled. I only need a first show and first hide, that's it.

What's the right way to remove the HTTP interceptor I added to the $httpProvider after the the first hide event has been fired?

I know we add the interceptor using a $httpProvider.interceptors.push() but I'm not sure how to pop it out when I no longer need that interceptor.

3
  • Have you found any way to grab this reference to $httpProvider.intercerceptors after the config phase? Commented Feb 9, 2015 at 1:27
  • 2
    You cannot get hold of a provider after the config phase, that's just not possible. Commented Feb 9, 2015 at 1:31
  • Rats. Thanks, I appreciate the fast response Commented Feb 9, 2015 at 1:32

2 Answers 2

3

I was going to put a bounty on this, as I had the same question. However....it looks as though the interceptors and responseInterceptors are simply arrays, according to the source code (lines 127 and 133 in the $httpProvider factory). There's no wrapper around this.

From what I can tell, you would either have to use pop() or any other array method. However, this means that you don't know what you're popping! Holding a reference to the object wouldn't really help, because you can't really do an array function on it, unless you decide to iterate based on equality (which could work, using indexOf or something else like Underscore).

Really, what Angular needs is a wrapper for this since you can't be sure that your interceptor is the last one on the list.

Sign up to request clarification or add additional context in comments.

4 Comments

I've tried this on a simple app where I know my interceptor is the only one (I also check the array length). Even though I remove my interceptor it's still being called. Btw, you can also use .slice() if you store the index of where you pushed your interceptor so you don't have to worry about pop'ing other interceptors.
@John-Philip Yeah, it seems that once Angular has bootstrapped, altering configuration is a no-no. Popping the interceptor won't work because Angular's already provided the service (using $get) and so the configuration has been seeded.
Just a thought. Is it possible to create the interceptor as an injectable object/function (factory or service) and then implement some kind of on/off mechanism for it?
@LarsJuelJensen It might be possible. You'd have to hold a reference to the object somewhere else for that to work. Using a closure, you could configure a flag inside the function you're passing for $get and do a noop if it's set to true.
1

The best solution that I've found is the one explained by jedd.ahyoung in his comment.

These are the steps.

Add two custom factories

angular.module('myModule.services', [])
/**
 * Save application information.
 */
.factory('Application', function($log) {
    return {};
})

/**
 * Custom Http Interceptor for the loader.
 */
.factory('myHttpInterceptor', function($rootScope, $q, Application) {
    return {
          request: function(config) {
            if(Application.enableLoader){
                $rootScope.$broadcast('loading:show');
            }
            return config;
          },

          requestError: function(rejection) {
              $rootScope.$broadcast('loading:hide');
              return $q.reject(rejection);
          },


          response: function(response) {
            $rootScope.$broadcast('loading:hide');
            return response;
          },

          responseError: function(rejection) {
              $rootScope.$broadcast('loading:hide');
              return $q.reject(rejection);
          }
    };
});

Add it in your config step

.config(function($httpProvider) {
    //loading interceptor
    $httpProvider.interceptors.push('myHttpInterceptor');
});

Enable/disable it when/where you want

Application.enableLoader = true;

$http({
  url: url,
  method: "GET"
}).success(function(data){
  $log.log("Data received and my loader is already closed :)");
  Application.enableLoader = false;
  $scope.retrieveBooks();
}).error(function(){
  $log.log("Error, but my loader is already closed :)");
  Application.enableLoader = false;
  $scope.retrieveBooks();
});


$scope.retrieveBooks = function(){
  $http({
    url: url,
    method: "GET"
  }).success(function(data){
    $log.log("Data received and my loader is not closed :(");
  }).error(function(){
    $log.log("Error, but my loader is not closed :(");
  });
};

1 Comment

Although your answer describes a way to let the application function as described by OP, the interceptor is still active and will be invoked on each http request. The question was how to remove it from $httpProvider.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.