Circular dependency found: $http <- $templateFactory <- $view <- $state

Mr. BigglesWorth picture Mr. BigglesWorth · Aug 26, 2014 · Viewed 22.6k times · Source

I have a current 401 check that I'm running with $location which is working fine. However I'd like to swap it over to $state and use ui-router instead. When I do so i get an error code as:

Circular dependency found: $http <- $templateFactory <- $view <- $state <- authHttpResponseInterceptor <- $http <- $compile

My currently code looks fine as I check certain paths and allow no logged in users to view them:

  /* Look for 401 auth errors and then redirect */
  .factory('authHttpResponseInterceptor',['$q','$location', function($q,$location) {

      return {
          response: function(response){
              if (response.status === 401) {
              }

              return response || $q.when(response);
          },
          responseError: function(rejection) {
              var reservedPaths = ['/','/login','/connect','/event'];
              if (rejection.status === 401 && _.contains(reservedPaths,$location.path().trim())) {
                  $location.path('/welcome');

              }
              return $q.reject(rejection);
          }
      };
  }])
  .config(['$httpProvider',function($httpProvider) {
      //Http Intercpetor to check auth failures for xhr requests
      $httpProvider.interceptors.push('authHttpResponseInterceptor');
  }]);

The code I added is as follows.:

  /* Look for 401 auth errors and then redirect */
  .factory('authHttpResponseInterceptor',['$q','$location', **'$state',** function($q,$location, **$state**) {

      return {
          response: function(response){
              if (response.status === 401) {
              }

              return response || $q.when(response);
          },
          responseError: function(rejection) {
              var reservedPaths = ['/','/mycube','/connect','/event'];
              if (rejection.status === 401 && _.contains(reservedPaths,$location.path().trim())) {
                  **$state.go('home');**

              }
              return $q.reject(rejection);
          }
      };
  }])
  .config(['$httpProvider',function($httpProvider) {
      //Http Intercpetor to check auth failures for xhr requests
      $httpProvider.interceptors.push('authHttpResponseInterceptor');
  }]);

Why would adding state cause this issue when it works fine with location?

Answer

aaronroberson picture aaronroberson · Aug 26, 2014

It appears that $state service is resulting in a circular dependency with the $http service. This may be caused by the fact that the templateFactory (see https://github.com/angular-ui/ui-router/blob/master/src/templateFactory.js) is being injected with the $http service in addition to the interceptor itself being composed with the $http service.

To get around this circular dependency issue, you can use the $injector service to wire up the $state service to your interceptor. See the revised code:

/* Look for 401 auth errors and then redirect */
module.factory('authHttpResponseInterceptor', ['$q','$location', '$injector', function($q, $location, $injector) {
    return {
        response: function(response){
            if (response.status === 401) {
            }

            return response || $q.when(response);
        },
        responseError: function(rejection) {
            var reservedPaths = ['/', '/mycube', '/connect', '/event'];
            if (rejection.status === 401 && _.contains(reservedPaths, $location.path().trim())) {
                var stateService = $injector.get('$state');
                stateService.go('home');
            }
            return $q.reject(rejection);
        }
    };
}]);

You can learn more about the $injector service here: https://docs.angularjs.org/api/auto/service/$injector

IMPORTANT

I would recommend using the state change events (see https://github.com/angular-ui/ui-router/wiki#state-change-events) to watch for errors using $stateChangeError and inspecting the error returned from the 401.