如何解决AngularJS中的“Maximum call stack size exceeded”问题

10

我正在使用AngularJS和Ui-Router尝试设置两个不同的首页,一个是给已登录用户的,另一个是给未登录用户的。但是我遇到了以下错误:

RangeError: Maximum call stack size exceeded

我运行了console.trace(),发现有一个问题导致状态无限循环(或类似的情况)。但我不知道如何修复它。

这是生成错误的代码。

.run(function ($rootScope, $state, $location, Auth) {
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState) {
      if(fromState.name === "") {
        if (Auth.isLoggedIn()) {
            $state.go('main');
            event.preventDefault();
        } else {
          $state.go('welcome');
          event.preventDefault();
        }
      } else {
         if (toState.authenticate && !Auth.isLoggedIn()) {
             $location.path('/login');
             event.preventDefault();
         }
      }
    });

据我所知,它似乎源于if(fromState.name === "")


你的循环在哪里?当一个循环无限运行时会发生这个错误。 - Mritunjay
抱歉,我不是说我有一个“循环”,我指的是错误在循环或者state.go可能在循环。我已经更新了问题,希望更加清晰明了。 - Daimz
确保在 $state.go 中不要更改 $rootScope 变量,这可能是原因,尽管通常会导致无限的 $digest 错误。这是一个奇怪的错误,最好尝试在 plunkr 中复制它,通常我发现尝试在我的环境之外复制它的过程有助于解决问题。此外,工作示例对于奇怪的问题更清晰! - Simeon Cheeseman
我其实不知道如何在plunkr上设置它以进行复制,我正在使用angular-fullstack,其中包括express和mongo。如果没有应用程序的其他部分,我无法使认证工作。因此,我无法创建一个公平的示例来引发错误。 - Daimz
2个回答

10

我创建了一个示例,用于测试默认页面和认证/非认证用户。类似的问题可以在这里看到。

首先这将是监听器:

app.run(function ($rootScope, $state, $location, Auth) {

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

      var shouldLogin = toState.data !== undefined
                    && toState.data.requireLogin 
                    && !Auth.isLoggedIn ;

      // NOT authenticated - wants any private stuff
      if(shouldLogin)
      {
        $state.go('login');
        event.preventDefault();
        return;
      }


      // authenticated (previously) comming not to root main
      if(Auth.isLoggedIn) 
      {
        var shouldGoToMain = fromState.name === ""
                          && toState.name !== "main" ;

        if (shouldGoToMain)
        {
            $state.go('main');
            event.preventDefault();
        } 
        return;
      }

      // UNauthenticated (previously) comming not to root public 
      var shouldGoToPublic = fromState.name === ""
                        && toState.name !== "public"
                        && toState.name !== "login" ;

      if(shouldGoToPublic)
      {
          $state.go('public');console.log('p')
          event.preventDefault();
      } 

      // unmanaged
    });
});

发生了什么?

  • 我们检查用户是否已登录。 如果没有,但需要访问非公共内容... 我们将重定向到登录页面
  • 用户已登录但不直接进入主页... 我们将转移他/她
  • 用户未登录,但不是前往公共页面... 我们将重定向
  • 否则...让它保持原样

这里是状态:

  $stateProvider
    // available for anybody
    .state('public',{
        url : '/public',
        template : '<div>public</div>',
    })
    // just for authenticated
    .state('main',{
        url : '/main',
        template : '<div>main for authenticated</div>',
        data : {requireLogin : true },
    })
    // just for authenticated
    .state('other',{
        url : '/other',
        template : '<div>other for authenticated</div>',
        data : {requireLogin : true },
    })
    // the log-on screen
    .state('login',{
        url : '/login',
        templateUrl : 'tpl.login.html',
        controller : 'LoginCtrl',
    })

请在这里这里检查plunker。


6
使用 $state.go("main", {}, {notify:false}); 可以在不触发 "$stateChangeStart" 事件时进行导航。
  // NOT authenticated - wants any private stuff
  if(shouldLogin)
  {
    $state.go("main", {}, {notify:false});
    event.preventDefault();
    return;
  }


  // authenticated (previously) comming not to root main
  if(Auth.isLoggedIn) 
  {
    var shouldGoToMain = fromState.name === ""
                      && toState.name !== "main" ;

    if (shouldGoToMain)
    {
        $state.go("main", {}, {notify:false});
        event.preventDefault();
    } 
    return;
  }

我认为这个解决方案要简单得多。而且它也完美地奏效了。 - Gjermund Bjaanes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接