Ui-Router $state.go внутри $on ('$ stateChangeStart') выкачивает бесконечный цикл

Я пытаюсь ввести логин в способ навигации пользователя через приложение.

Я притворяюсь, что перенаправляет пользователя на страницу, прежде чем перейти к странице входа, если эта страница соответствует определенным требованиям.

Предотвращение события из $stateChangeStart останавливает изменение состояния, как ожидалось, но когда я запускаю $state.go('in_somewhere'), я ввожу бесконечный цикл

Моя версия angular - 1.3.1, а ui-router - последняя

.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 {

    };
  });

Ответ 1

В общем, я бы сказал, пусть перенаправление ($state.go()) только при необходимости. В других случаях выйдите из прослушивателя событий:

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')

Это упрощенная логика, но показывает, что мы должны перейти на выполнение только в случае необходимости. Есть еще несколько примеров с более подробной реализацией и plunkers:

Как указано в комментарии, был plunker, который я изменил следующим образом здесь

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

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

И это больше не. Пожалуйста, проверьте здесь

Ответ 3

Вы должны использовать опцию уведомления:

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

Это предотвратит запуск stateChangeStart снова.

Ответ 4

Я просто использовал $location.path('every/where') вместо $state.go('every/where')

:).

Ответ 5

Бесконечный цикл частично вызван

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

.., в котором говорится, что if вы переходите в состояние входа, then перейдите в состояние входа.

... И вызов event.preventDefault(), поскольку первая строка в обработчике событий не помогает. Когда вы используете go() для перехода на экран входа в систему (или где-либо еще), это изменение состояния также предотвращается event.preventDefault(). Его следует использовать только в if.

Весь ваш обработчик $stateChangeStart должен быть...

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

... который читается естественно. "if вы не вошли в систему, и вы уже не ходите на экран входа в систему, then остановите свое действие, я запомню, куда вы хотите пойти, и теперь вы переходите на экран входа в систему."

Позже ваш объект Auth выдаст $state.go(Auth.desiredState), когда он будет удовлетворен пользователем.

Ответ 6

Это работает для меня. Ниже код помогает избавиться от бесконечного цикла

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