10

I am using AngularJs and Ui-Router and I am trying to set two different home pages, one for users that are logged in and the other for users that aren't. But I am getting the following error:

RangeError: Maximum call stack size exceeded

I ran console.trace() and I can see that there is an issue which is causing the states to loop infinitely (or something like that). BUt I have no idea how to fix it.

Here is the code that is generating the error.

.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();
         }
      }
    });

From what I can tell it seems to stem from if(fromState.name === "")

4
  • where's your loop. When a loop runs for infinitive this error occurs Commented Jul 30, 2014 at 4:06
  • Sorry I didn't mean I have a 'loop' I meant more the error is looping or the state.go perhaps is looping. I have updated the question, hopefully it's a bit more clear. Commented Jul 30, 2014 at 4:09
  • Make sure you aren't changing $rootScope variables inside $state.go, that could be the cause, though normally that causes an infinite $digest error. It's an odd error, best would be to try replicate it in a plunkr, normally I find the process of trying to replicate it outside of my environment helps figure out the problem. Also working examples are clearer for weird problems! Commented Jul 30, 2014 at 4:49
  • I don't actually know how I can set it up on plunkr to replicate, I am using angular-fullstack and which includes express and mongo I can't get the auth to work with out the rest of the app. So I can't make a fair example that will throw the error. Commented Jul 30, 2014 at 4:56

2 Answers 2

10

I've created an example, playing with default pages and auth/unauth user. Similar issue could be seen here

Firstly this would be the listener:

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
    });
});

What is happening?

  • We check if user is logged in. If not, but requires access to not public stuff... we redirect to login
  • user is logged in but not going directly to main... we transfer him
  • user is not logged in, but not going to public ... we redirect
  • else... let it be

And here are states:

  $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',
    })

Check plunker here or here

Sign up to request clarification or add additional context in comments.

Comments

6

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

  // 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;
  }

1 Comment

This solution is way simpler the way I see it. It also worked perfectly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.