Rosher Consulting

Software Consulting and Development Services

Angular Tips: Displaying an Ajax loading spinner using a custom Interceptor

I think one of the reasons Angular has become so popular, so quickly is because it has a series of conventions that make extending it really easy.

A common requirement for any UI is to let the user know when something is happening, such as when making ajax calls to the server and a common way to do this is to display a spinner. Prior to using AngularJS I would have handled this by creating my own module for talking to the server and then all other modules would have had to call this module, nothing wrong with this approach at all, but Angular already has its own module for dealing with ajax requests: ‘$http’, so we don’t really want to re-invent the wheel and create another module to sit on top of this.

This is where Angular’s extensibility comes in as we can handle this by hooking into Angular’s ‘$http’ service by creating our own interceptor, which allows us to intercept the request to the server, display the spinner, handle the response back and hide it again

Here’s a template for a custom interceptor that displays an ajax spinner when a request starts and hides it when the request finishes:

   1:  var App = angular.module('myApp', []).
   2:      config(['$httpProvider', function ($httpProvider) {
   3:          
   4:          $httpProvider.interceptors.push('myHttpInterceptor');
   5:          
   6:      }]).factory('myHttpInterceptor', ['$q', function ($q) {
   7:          var numRequests = 0;
   8:          var ajaxSpinner = $("#ajaxSpinner");
   9:          var hide = function (r) {
  10:              if (!--numRequests) {
  11:                  ajaxSpinner.hide();
  12:              }
  13:              return r;
  14:          };
  15:   
  16:          return {
  17:              'request': function(config) {
  18:                  numRequests++;
  19:                  ajaxSpinner.show();
  20:   
  21:                  return config;
  22:              },
  23:   
  24:              'response': function(response) {
  25:                  return hide(response);
  26:              },
  27:   
  28:              'responseError': function(response) {
  29:                  return $q.reject(hide(response));
  30:              }
  31:          };
  32:    }]);

This is pretty straightforward and adds a nice effect to our UI, but we can also take this a step further.

Whenever I want to send messages back from the server indicating success or failure I always send a standard object back, such as:

   1:  {
   2:      Error: true,
   3:      Message: 'An error occured saving XYZ'
   4:  }

Now I could handle this at the point I make the call, but since this is a standard message, this seems like a perfect fit for our custom interceptor.

The following code not only shows and hides our ajax spinner, it also checks the response and if necessary displays an alert to the user, saving us from having to handle this everywhere in our code:

   1:  var App = angular.module('myApp', []).
   2:      config(['$httpProvider', function ($httpProvider) {
   3:          
   4:          $httpProvider.interceptors.push('myHttpInterceptor');
   5:          
   6:      }]).factory('myHttpInterceptor', ['$q', function ($q) {
   7:          var numRequests = 0;
   8:          var ajaxSpinner = $("#ajaxSpinner");
   9:          var hide = function (r) {
  10:              if (!--numRequests) {
  11:                  ajaxSpinner.hide();
  12:              }
  13:              return r;
  14:          };
  15:   
  16:          return {
  17:              'request': function(config) {
  18:                  numRequests++;
  19:                  ajaxSpinner.show();
  20:   
  21:                  return config;
  22:              },
  23:   
  24:              'response': function(response) {
  25:                  if (response && response.data && response.data.Error && 
  26:                          response.data.Error === true && response.data.Message) {
  27:                      alert(response.data.Message);
  28:   
  29:                      return $q.reject(hide(response));
  30:                  }
  31:   
  32:                  if (response && response.data && response.data.Error === false && 
  33:                          response.data.Message) {
  34:                      alert(response.data.Message);
  35:                  }
  36:   
  37:                  return hide(response);
  38:              },
  39:   
  40:              'responseError': function(response) {
  41:                  if (!response)
  42:                      return $q.reject(hide(response));
  43:                      
  44:                  if (response.data && response.data.Error && 
  45:                          response.data.Error === true && response.data.Message) {
  46:                      alert(response.data.Message);
  47:                  } else {
  48:                      alert('Sorry, there was an error.');
  49:                  }
  50:                      
  51:                  return $q.reject(hide(response));
  52:              }
  53:          };
  54:    }]);

This is just a couple of examples of what we can do with a custom interceptor to help remove boilerplate from our code and hopefully it’s given you some ideas for your own code.

Comments are closed