17

I'm trying to introduce login into the way the user navigates accross the application.

I pretend to redirect the user to the page were he was before he navigate to the login page if that page meets specific requirements

Preventing the event from the $stateChangeStart stop's the state change like expected but when i run the $state.go('into_somewhere') i enter an infinit loop

My angular version is 1.3.1 and the ui-router is the latest

.factory('RouteHistory', function ($rootScope,$log, $state, Auth, $urlRouter, $timeout) {

    // after the user enter a page
    var currentState = '';

    // when the user is trying to access a page that he has not permissions
    // or that requires the user to be logged in
    var pendingState = '';

    var isMenuTogglerVisible = false;
    var skipFromStateVal = true;

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

      event.preventDefault();



      if (toState.name == 'login' && fromState.name != 'login'){
        $log.log('Ui-router: changing to login');
        // $urlRouter.sync();
        $state.go('login')
        //pendingState = fromState;
        //$log.log('Peding state updated to:' + pendingState.name );
        //$urlRouter.sync();
      }

      if (fromState.name == 'login' && Auth.isLoggedIn()) {
        $log.log('Ui-router: going from login');
        //$state.go(fromState.name);
        $timeout(function(){
          // $state.go('home', null, {/*reload: true, location: 'replace'*/});
          $state.go('browse-machine');
          //$urlRouter.sync();
        },2000)
      }



      $log.log({
        'toState': toState,
        'toParams': toParams,
        'fromState': fromState,
        'fromParams': fromParams
      })

    })


    return {

    };
  });
Lothre1
  • 3,523
  • 7
  • 43
  • 64
  • Hello, Could you show what th log says in your web console ? – rllola Nov 07 '14 at 08:35
  • actually it doesn't return any error. i just used $log.log(toState, fromState) inside the event handler to output the object. That's when i noticed that event.preventDefault() followed by a $state.go('stateName') was causing an infinit loop because i saw the log mesage printed undreads of times – Lothre1 Nov 07 '14 at 10:29
  • for example, in my sample code, if i try to access a page coming from a state named 'login' i would see 'Ui-router: going from login' print undread of times. – Lothre1 Nov 07 '14 at 10:31

6 Answers6

13

In general I would say, let's redirect ($state.go()) only if needed. In other cases, get out from the event listener:

if (toState.name === 'login' ){
  // doe she/he try to go to login? - let him/her go
  return;
}

if(Auth.isLoggedIn()){
   // is logged in? - can go anyhwere
   return;
}

// else
$state.go('login')

This is simplified logic, but shows, that we should change to execution only if needed. There are some other examles with more detailed implementation and plunkers:

As provided in the comment, there was plunker, which I changed like this here

...
// three new lines
if (toState.name === 'specialRoute'){
  return;
}

if (fromState.name=='route1'){
  event.preventDefault();
  $state.go('specialRoute')
}

And this is not looping anymore. Please, check it here

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • 1
    please take a look at this plnkr: http://plnkr.co/edit/u18KQc?p=preview. Open the terminal to see the infinite loop that i was talking about – Lothre1 Nov 07 '14 at 13:22
  • 1
    I tried to show how to ... in a fork, and updated the answer. Does it help? – Radim Köhler Nov 07 '14 at 13:56
  • 1
    Yes, it seems to fix the problem, but what was cauzing the infinite loop? – Lothre1 Nov 07 '14 at 14:01
  • 2
    The point is, that we firstly should solve: "is it already redirected?" I.e.: was already trying to reach the state `'specialRoute'`? And if yes... then just return.. let UI-Router to continue. And that was missing. So after redirect was triggered firstly then **again and again and again...** now it is checked... and if `'specailRoute'` is already selected... no other redirect! Does it help? – Radim Köhler Nov 07 '14 at 14:03
  • Great to see that, Enjoy amazing `UI-Router`, sir ;) ;) – Radim Köhler Nov 07 '14 at 14:09
  • Thats a great hack – Mukund Gandlur Mar 12 '16 at 17:34
8

You should use the notify option :

$state.go('your.state',{ your params },{notify: false});

This will prevent stateChangeStart to fire again.

Rémy DAVID
  • 4,343
  • 6
  • 25
  • 27
6

This answer helped me:

$urlRouterProvider.otherwise( function($injector, $location) {
            var $state = $injector.get("$state");
            $state.go("app.home");
        });

Original: Why does AngularJS with ui-router keep firing the $stateChangeStart event?

Community
  • 1
  • 1
Andi Giga
  • 3,744
  • 9
  • 38
  • 68
1

I simply used $location.path('every/where') instead of $state.go('every/where')

:) .

  • 1
    Minor issue with using $location inside of $stateChangeStart: the state you wanted to avoid will start loading and possibly displaying before the redirect happens. – Ron Newcomb Sep 16 '16 at 21:59
1

The infinite loop is partly caused by

if (toState.name == 'login' ...) { $state.go('login'); ...

..which says if you're going to the login state, then go to the login state.

...And calling event.preventDefault() as the first line in the event handler doesn't help. When you use go() to go to the login screen (or anywhere else), that state change is also prevented by event.preventDefault(). It should only be used within an if.

Your entire $stateChangeStart handler should instead be...

if (!Auth.isLoggedIn() && toState.name != 'login') {
    event.preventDefault();
    Auth.desiredState = toState.name;
    $state.go('login');
}

...which reads naturally. "If you're not logged in and you're not already going to the login screen, then stop what you're doing, I'll remember where you wanted to go, and you now go to the login screen."

Later your Auth object will issue a $state.go(Auth.desiredState) when it's satisfied with the user.

Ron Newcomb
  • 2,886
  • 21
  • 24
1

It works for me, Below code helps to get rid of infinite loop

  let firstPass = true;
  $scope.$on('$stateChangeStart', function(event, toState, toParams) {
    if ($scope.addForm.$dirty && firstPass) {
      event.preventDefault();
      ConfirmationDialog.openNavigateAwayConfirmationModal().then(function () {
        firstPass = false;
        return $state.go(toState, toParams);
      });
      firstPass = true;
    }
  });