Подтвердить что ты не робот

Angular JS ui-router как перенаправить дочернее состояние от родителя?

При использовании onEnter для перенаправления в состояние, если новое состояние является дочерним по отношению к текущему состоянию, возникает бесконечный цикл.

Пример:

$stateProvider
  .state 'inventory',
    url: '/inventory'
    templateUrl: 'views/inventory.html'
    controller: 'InventoryCtrl'
    onEnter: () ->
      $state.go 'inventory.low'
  .state 'inventory.low',
    url: '/low'
    templateUrl: 'views/inventory-table.html'
    controller: 'LowInventoryCtrl'

Когда:

$state.go 'inventory.low'
Вызывается

, состояние inventory повторно инициализируется, заставляя его снова вызываться = бесконечный цикл.

Однако, если состояние переадресации:

$state.go 'otherStateThatIsNotAChild'

Эта проблема не возникает. Я предполагаю, что родительское состояние повторно инициализируется, но почему?

  • Почему родительское состояние повторно инициализируется при вызове .go в дочернем состоянии?
  • Как же тогда вы отредактируете перенаправление на дочернее состояние?
4b9b3361

Ответ 1

1) Почему родительское состояние повторно инициализируется, когда .go вызывается на дочернее состояние?

Пока переход находится в процессе, любой $state.go/transitionTo приведет к замене текущего перехода процесса. Переход в процессе, который заменяется, немедленно отменяется. Поскольку ваш первоначальный переход на inventory не завершен к тому времени, когда вызываются все состояния onEnters, исходный переход отменяется, и новый переход на inventory.low запускается из ранее активного состояния.

См. ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897...

2) Как же вы могли бы отредактировать перенаправление к дочернему состоянию?

Вы могли бы...

  • Оберните $state.go в $timeout(), чтобы исходный переход завершился до перенаправления.
  • Вместо этого вызовите $state.go с вашего контроллера. UI-маршрутизатор вызывает контроллер из директивы ui-view ПОСЛЕ завершения перехода.

В любом случае, будьте уверены, что вы хотите, чтобы ваше приложение перенаправлялось именно так. Если пользователь перешел непосредственно к любому другому дочернему состоянию инвентаря (например, inventory.high), перенаправление все равно будет происходить, заставив их inventory.low, которые не были бы тем, что они предполагали.

Ответ 2

У меня была та же проблема. Простое решение, которое я нашел, это прослушивание изменений состояния и перенаправление на ваше дочернее состояние.

Это своего рода хак, который по умолчанию перенаправляет маршруты в дочерние состояния. Не нужно делать url: '' или $state.go(), они работают неправильно.

Итак, в config файле:

$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) {
  if (toState.redirectTo) {
    event.preventDefault();
    $state.go(toState.redirectTo, toParams);
  }
});

В state файле:

.state('home', {
  url: '',
  redirectTo: 'home.list'
});

Я сделал пример по gist: https://gist.github.com/nikoloza/4ab3a94a3c6511bb1dac

Ответ 3

Вам нужно отступить и подумать о том, чего вы пытаетесь достичь. Какой смысл иметь состояние, когда все это происходит, перенаправляется на дочернее состояние?

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

Что касается второго вопроса, я работал над несколькими крупными приложениями angular, и до сих пор мне не приходилось этого делать.

ОК, верьте или нет, как бы я не хотел это говорить, прямо сейчас я столкнулся с сценарием, где мне нужно было это сделать. У меня есть маршрут profile/userName (технически это должен быть profile/userName/details) и маршрут profile/userName/products, я хотел иметь представление мастера для обоих состояний, но в то же время я хотел, чтобы маршрут profile/userName имел чистый URL, например: profile/mike62, NOT profile/mike62/details. Поэтому я закончил это:

.state('publicProfile', { url: '/profile/{username}'})//this is the base/shell state
.state('publicProfile.details',{url:null})//I want this to be my de-facto state, with a clean URL, hence the null url
.state('publicProfile.products', {url:'/products/{exceptProductId:/?.*}'})

Закончив достижение этого, существует много способов:

в моем контроллере состояния publicProfile (это базовое состояние):

$scope.state = $state;
$scope.$watch('state.current', function(v) {
       if(v.name=='publicProfile') {//want to navigate to the 'preferred' state if not going to publicProfile.products
            $state.go('publicProfile.details');
       };
}, true);

Да, он чувствует себя взломанным, но теперь я знаю, что есть некоторые крайние случаи, когда мы хотим это сделать, несмотря на то, что мы могли бы полностью пересмотреть наш государственный дизайн. Другим, более грязным способом было бы проверить текущее состояние в $timeout с небольшой задержкой внутри базового состояния, если мы не находимся в состоянии publicProfile.products, мы переходим к нашему предпочтительному/де-факто состоянию publicProfile.details.

Ответ 4

Похоже, вы пытаетесь установить дочернее состояние по умолчанию.

Это часто задаваемый вопрос о ui-router: Как настроить дочернее состояние по умолчанию/индекс

tl; dr должен использовать абстрактные состояния с помощью настроек abstract: true в родительском состоянии. Если вы добавите несколько дочерних состояний, по умолчанию будет установлено первое дочернее состояние.

$stateProvider

  .state('inventory', {
    abstract: true,
    url: '/inventory',
    templateUrl: 'views/inventory.html',
    controller: 'InventoryCtrl'
  }).

    .state('inventory.low', {
      url: '/low',
      templateUrl: 'views/inventory-table.html',
      controller: 'LowInventoryCtrl'
    });

Ответ 5

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

onEnter: ['$state', function($state){
    if ($state.transition) {
        $state.transition.finally(function() {
            $state.go('home.my.other.state', {})
        });
    }
}]

Ответ 6

После объяснения @Chris T кажется, что самым чистым решением является прослушивание события $stateChangeSuccess:

redirects =
  inventory: 'inventory.low'

$rootScope.$on '$stateChangeSuccess', (e, toState) ->
  redirect = redirects[toState.name]
  $state.go redirect if redirect

Если перенаправления будут содержать перенаправления маршрутов, которые могут произойти. Спасибо всем!

Ответ 7

Не заставляйте низкое состояние инвентаря дочерним состоянием.

$stateProvider
    .state 'inventory',
        url: '/inventory'
        templateUrl: 'views/inventory.html'
        controller: 'InventoryCtrl'
        onEnter: () ->
            $state.go 'lowinventory'
    .state 'lowinventory',
        url: '/inventory/low'
        templateUrl: 'views/inventory-table.html'
        controller: 'LowInventoryCtrl'