5
votes

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

3
didi you find the fix? - Kumar Sambhav
@KumarSambhav Yes, I was able to workaround this, see my answer below to fit your specific use-case. - Diosney

3 Answers

5
votes

One work around that has helped me in the past is to use $injector to get your dependency at runtime instead of at configuration time. So, something like:

  /* @ngInject */
  function HttpError500Interceptor($q,
                                   $injector,
                                   $filter) {
    function responseError(responseError) {
      var $mdToast = $injector.get('$mdToast');

When your cyclic dependency does not cause problems, which it probably doesn't in this case, this will do the trick.

4
votes

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.

0
votes

What you should do is create a function that brings you the toaster at run time

  var getToaster = ()=>{

    var toaster = $injector.get('$mdToaster');
    return toaster;
}

Now call it only when you need it - this will provide a work around the dependency circular