Tagged: interceptor

Waiting indicator using AngularJS interceptors

There are a number of needs that routinely come up when putting together a web UI.   This indicator notifies the user that action is occurring in the background, and goes away automatically when everything is done.
please wait
This happens automatically when your AngularJS app is requesting information via http. This is similar to other code on the web, but is modified it to suit the following needs:

  • Displays wait indicator automatically when http requests are initiated.
  • Tracks the number of requests, and dismisses the indicator when all are resolved.
  • A delay of one second prevents indicator from displaying during angular loading views and quick requests.
  • No jquery (yay)

Here it is on JSFiddle

If you’re looking for code (and you probably are), I recommend taking a look at the JSFiddle link above – it integrates the code, css and html.

We’re using several key AngularJS features here:

Directive

The directive is Angular’s way to hook UI elements to javascript functionality. Much of the logic is located here.

myApp.directive("loadingIndicator", function (loadingCounts, $timeout) {
    return {
        restrict: "A",
        link: function (scope, element, attrs) {
            scope.$on("loading-started", function (e) {
                loadingCounts.enable_count++;
                console.log("displaying indicator " + loadingCounts.enable_count);
                //only show if longer than one sencond
                $timeout(function () {
                    if (loadingCounts.enable_count > loadingCounts.disable_count) {
                        element.css({ "display": "" });
                    }
                }, 1000);
            });
            scope.$on("loading-complete", function (e) {
                loadingCounts.disable_count++;
                console.log("hiding indicator " + loadingCounts.disable_count);
                if (loadingCounts.enable_count == loadingCounts.disable_count) {
                    element.css({ "display": "none" });
                }
            });
        }
    };
});

Interceptors

Allows us to tie into http-related events and run our code at that time. In this case we are hooking into both the request, response and responseError (just in case). Here’s another post on interceptors.

myApp.config(function ($httpProvider) {
    $httpProvider.interceptors.push(function ($q, $rootScope) {
        return {
            'request': function (config) {
                $rootScope.$broadcast('loading-started');
                return config || $q.when(config);
            },
            'response': function (response) {
                $rootScope.$broadcast('loading-complete');
                return response || $q.when(response);
            },
            'responseError': function (rejection) {
                $rootScope.$broadcast('loading-complete');
                return $q.reject(rejection);
            }
        };
    });
});

Broadcast

Broadcast is the way we can fire (and respond to) our own events. As you can see, the interceptor events use $broadcast messages to trigger the logic (located in the directive) to show or hide the indicator.

You can implement this solution from the code on this page, if you include the following:

myApp.factory('loadingCounts', function () {
    return {
        enable_count: 0,
        disable_count: 0
    }
});

See it in action: Here it is on JSFiddle

AngularJS Interceptors for logging service calls

I should start a series of angularJS posts called “stupid angularJS tricks”, because I’m always trying to figure out how to do this or that. Thank goodness for other peoples’ blogs!

In my project, I’m building out a suite of apps which (true to AngularJS form) get their data from services. It’s handy to log all service calls, to make sure caching is working, etc. Ultimately there will be quite a few service calls, so we may want to keep track of them, and it’s certainly handy for debugging.

Prior to my discovery of interceptors, I would write an entry similar to this just before every $http.get or $http.post call, similar to this insanely simplified example:

console.log('WS GET service ' + svcUrl);
$http.get(svcUrl).
  success(function (result, httpstatus, headers) {
  });

Although this does the job, it’s not foolproof – I might forget to include it into future code (resulting in an unlogged call), or worse, I may change my get to a post and the log message would be wrong.

Enter the interceptor.

This lovely mechanism allows us to “intercept” an http call and run our custom code at key points. In this case I want to log something prior to running any $http call. There are various other places I can plug in. It’s all here (search on “interceptor”).

Design your factory for injection

In this case, I’m just plugging into the “request” event, which will fire before the request actually occurs. We receive a configuration object which describes the request, and I fashioned a very crude filter. Why? As I discovered, all requests from angular to include views also come through here. I don’t want those logged – just my web service requests.

myapp.factory('serviceLogger', function () {
    return {
        request: function (config) {
            //weed out loading of views - we just want service requests.
            if (config.url.indexOf('html') == -1) {
                console.log("HTTP " + config.method + " request: " + config.url);
            }
            return config;
        }
    };
});

The interceptor above will do its work prior to the request being made.  There are 4 places where you can use this method to intercept an HTTP request (from the documentation):

  • request: (as above)  Interceptor gets called with http config object. The function is free to modify the config object or create a new one. The function needs to return the config object directly, or a promise containing the config or a new config object.
  • requestError: Interceptor gets called when a previous interceptor threw an error or resolved with a rejection.
  • response: interceptors get called with http response object. The function is free to modify the response object or create a new one. The function needs to return the response object directly, or as a promise containing the response or a new response object.
  • responseError: Interceptor gets called when a previous interceptor threw an error or resolved with a rejection.  Good place for global handling of exceptions thrown in web services!

If you implement requestError or responseError, be sure to return a $q rejection – otherwise the code in your controller will not recognize the error.

Add the functionality to the $httpProvider

Now you’ve got your interceptor – but you still need to make sure your app is using it!   Note that you can only do this in the config section – this is where the $httpProvider is available.

myapp.config(function ($routeProvider, $httpProvider) {
      //you probably have some sort of a routing table here.
      $routeProvider
      .when('/something', { templateUrl: 'views/something.html', controller: 'somethingCtl' })
      .when('/anotherthing', { templateUrl: 'views/anotherthing.html', controller: 'anotherthingCtl' })
      .otherwise({ redirectTo: '/something' });

      //here's where you add your interceptor
      $httpProvider.interceptors.push('serviceLogger');
  })

That’s it!

Be sure to check out all the other stuff that interceptors can do!