Search code examples
javascriptangularjscircular-dependencyangular-http-interceptorsangularjs-material

Using `$mdToast` inside an interceptor triggering circular dependency


Question:

How can I use $mdToast inside an interceptor without triggering the error?

Setup:

Interceptor definition:

(function () {
  'use strict';

  angular
    .module('app.components.http-errors-interceptors')
    .factory('HttpError500Interceptor', HttpError500Interceptor);

  /* @ngInject */
  function HttpError500Interceptor($q,
                                   $mdToast,
                                   $filter) {

    var interceptor           = {};
    interceptor.responseError = responseError;

    function responseError(responseError) {
      if (responseError.status === 500) {
        $mdToast.show($mdToast.simple()
                              .content($filter('translate')('APP.COMPONENTS.HTTP_ERRORS_INTERCEPTORS.500'))
                              .position('bottom right')
                              .hideDelay(5000));
      }
      return $q.reject(responseError);
    }

    return interceptor;
  }
})();

Interceptor config:

(function () {
  'use strict';

  angular
    .module('app.components.http-errors-interceptors')
    .config(moduleConfig);

  /* @ngInject */
  function moduleConfig($httpProvider) {
    $httpProvider.interceptors.push('HttpError500Interceptor');
  }
})();

Issue:

When I load the application it triggers the following error:

Uncaught Error: [$injector:cdep] Circular dependency found: $http <- $templateRequest <- $$animateQueue <- $animate <- $$interimElement <- $mdToast <- HttpError500Interceptor <- $http <- $templateFactory <- $view <- $state


Solution

  • Neither of the provided solutions worked for me, so I'm posting here what I did so anyone with the same issue can have a range of workarounds to use.

    What I really wanted was to have a common component to handle HTTP interceptors named interceptors and show the messages directly from the module, and happily, since the final solution is more elegant, it triggered this error while injecting the $mdToast service.

    The solution I came later, which I already said, is more elegant that my first fix to the issue is:

    • to have a common component to handle HTTP interceptors named interceptors,
    • to have a common component to handle global notifications named notifications-hub.

    Then, in the interceptors module, I trigger a global event with:

    $rootScope.$broadcast('notifications:httpError', responseError);

    Then, in the notifications-hub module, I registered on the event and used the $mdToast, which was injected without errors in the notifications service:

    $rootScope.$on('notifications:httpError', function (event, responseError) { NotificationsHubService.processErrorsAndShowToast(responseError); });

    NotificationsHubService is the service injecting $mdToast.

    Conclusion:

    I used a global event as a glue between the low-level interceptors and the notifications subsystem to overcome the issue.

    Hopefully it will be of use for anyone else.