Error: Maximum call stack size exceeded on Angular

2020-05-02 04:03发布

Basically what I'm trying to do is change the state depending on user authentication status.

When I do $state.go(); without $rootScope, I can redirect to the page without the error but when I do with

$rootScope.$on('$stateChangeStart', function (event) { $state.go(); event.preventDefault() })

Below Is the Controller Code:

DiaryDashboard.controller('AppController', 
  ['$scope', '$rootScope', '$localStorage', '$http', '$state'
  , function ($scope, $rootScope, $localStorage, $http, $state) {

  $rootScope.$on('$stateChangeStart', function (event, toState) {

    if ($localStorage.userdetails &&
        $localStorage.userdetails.isAuthenticated == true) {

        //Check for Authentication state
        //If not redirect to the state in the else clause
        //Populate the $rootScope with user details
        $rootScope.userdetails = $localStorage.userdetails;

        //Switch to the parent state
        $state.go();
        event.preventDefault();

    } else {

        //If User not authenticated
        //GO to the Authentication State
        $state.go('auth');
        event.preventDefault();

    }
  })

}]);

5条回答
戒情不戒烟
2楼-- · 2020-05-02 04:09

Use $state.go("main", {}, {notify:false}); for not notify to "$stateChangeStart"event and not allow the event loop.

$state.go("main", {}, {notify:false});
查看更多
Animai°情兽
3楼-- · 2020-05-02 04:22

You are basically creating a state change interceptor.

The thing is, on every one of your branches in '$stateChangeStart' you have a $state.go . Since this is an interceptor, every time you invoke $state.go , the handler for $stateChangeStart will be invoked, and this is why you get this invocation loop that ends with that error.

You need to write the handler in a way that has at least one branch that does not invoke $state.go

Here is how we implemented it:

//The list of states that do not need authentication (like auth)
var authorizedStates = ['auth', 'someOtherAuthorizesState', 'etc'];

// This method reports if a state is one of the authorizes states
var isChildOfAuthorizedStated = function (stateName) {
  var isChild = false;
  authorizedStates.forEach(function (state) {
    if (stateName.indexOf(state) === 0) {
      isChild = true;
    }
  });
  return isChild;
};

$rootScope.$on('$stateChangeStart',
  // The stateChangeStart handler
  function (event, toState, toParams, fromState, fromParams) {
    // Check if the future state is not an authorized state 
    // and if the user is not authenticated. 
    if (!isChildOfAuthorizedStated(toState.name) && !Auth.isAuthenticated()) {
      event.preventDefault();
      $state.go('auth');
    }
);
查看更多
Juvenile、少年°
4楼-- · 2020-05-02 04:28

The most important thing here is to understand this:

Do not redirect if not needed. Other words, if user is already redirected to intended state - we should leave... There is a working plunker with similar solution.

Check this Q & A:

Angularjs ui-router. How to redirect to login page

the adjusted code:

$rootScope.$on('$stateChangeStart', function (event, toState) {

    var isNavigatingToAuth = toState.name === "Auth";

    if(isNavigatingToAuth){

       return; // no need to redirect 
    }

    if ($localStorage.userdetails &&
        $localStorage.userdetails.isAuthenticated == true)
    ...
查看更多
在下西门庆
5楼-- · 2020-05-02 04:34

You are creating a redirection loop. You need to check whether the new route is already the route where you want to send your client to.

$rootScope.$on("$routeChangeStart", function (event, next, current) {
    if (next == 'my-auth-page') {
        // already going to auth page no redirect needed
    } else {
        // not going to auth page, we should redirect now
        $location.path('my-auth-page');
    } 
});
查看更多
手持菜刀,她持情操
6楼-- · 2020-05-02 04:34

Your code is going in infinite as you are doing $state.go in both cases, You could should not do $state redirection user is authorized & second condition is proper which is in else block

   if ((!$localStorage.userdetails || !$localStorage.userdetails.isAuthenticated) 
           && toState.name != 'auth') { //<--this condition will stop infinite loop
        //If User not authenticated
        //GO to the Authentication State
        $state.go('auth');
        event.preventDefault();
    }

Update

Other thing which I wanted to point out about your code is $stateChangeStart should be inside a run block(which initialize exactly after config block) so that It would be common for all state & will initialized this condition at the starting of your app

Code

app.run(function($rootScope){
    $rootScope.$on('$stateChangeStart', function (event, toState) {

        if ($localStorage.userdetails && $localStorage.userdetails.isAuthenticated == true) {
              if ((!$localStorage.userdetails || !$localStorage.userdetails.isAuthenticated) 
               && toState.name != 'auth') { //<--this condition will stop infinite loop
            //If User not authenticated
            //GO to the Authentication State
            $state.go('auth');
            event.preventDefault();
        }
    })
});

Also do use service to store UserDetails using $rootScope in AngulaJs is considered as bad code practice.

查看更多
登录 后发表回答