角度JS ui路由器如何从父进程重定向到子进程?

时间:2021-07-11 12:49:33

When using onEnter to redirect to a state, if the new state is a child of the current state, an infinite loop occurs.

当使用onEnter重定向到一个状态时,如果新状态是当前状态的子状态,就会发生无限循环。

Example:

例子:

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

When:

当:

$state.go 'inventory.low'

Is called, the state inventory is re-initialized, causing it to be called again = infinite loop.

被调用,状态库存被重新初始化,导致它被再次调用=无限循环。

However, if the redirect state is:

但是,如果重定向状态是:

$state.go 'otherStateThatIsNotAChild'

This issue does not occur. I assume that the parent state is being re-initialized, but why?

这个问题不会发生。我假设父状态正在被重新初始化,但是为什么呢?

  1. Why is the parent state being reinitialized when .go is called on a child state?
  2. 当.go被调用到子状态时,为什么父状态被重新初始化?
  3. How then, would you handle redirecting to a child state?
  4. 那么,如何处理重定向到子状态呢?

7 个解决方案

#1


19  

1) Why is the parent state being reinitialized when .go is called on a child state?

1)为什么在子状态上调用.go时要重新初始化父状态?

While a transition is in process, any $state.go/transitionTo will cause the currently in process transition to be Superseded. An in-process transition that is superseded is cancelled immediately. Since your original transition to inventory is not completed by the time all the states' onEnters are called, the original transition is cancelled and a new transition to inventory.low is started from the previously active state.

当转换正在进行时,任何$状态。go/transitionTo会导致当前正在进行的转换被取代。被取代的进程内转换立即被取消。由于您最初向库存的转换在调用所有状态的承销商时还没有完成,因此取消了最初的转换,并重新转换到库存。low是从先前的活动状态开始的。

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

参见ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897…

2) How then, would you handle redirecting to a child state?

2)如何处理重定向到子状态?

You could...

你可以…

  • Wrap $state.go in a $timeout() to allow the original transition to complete before redirecting.
  • 包装状态。进入$timeout()允许在重定向之前完成原始转换。
  • Call $state.go from your controller instead. UI-router invokes the controller from the ui-view directive AFTER the transition is completed.
  • 称美元的状态。从控制器开始。在转换完成后,UI-router从ui-view指令调用控制器。

In any case, be very sure you want your app to redirect like this. If a user navigated directly to any other child state of inventory (such as inventory.high), the redirect will still occur, forcing them to inventory.low which would not be what they intended.

无论如何,一定要让你的应用像这样重定向。如果用户直接导航到任何其他的子目录(如inventory.high),那么重定向仍然会发生,迫使它们进行编目。低,这不是他们想要的。

#2


9  

I had the same problem. The simple solution I found is to listen state changes and redirect to your child state from there.

我也有同样的问题。我找到的一个简单的解决方案是监听状态的变化并从那里重定向到您的子状态。

It's kind of hack that makes routes redirecting to child states by default. It's not needed to do url: '' or $state.go(), they don't work correctly.

这是一种黑客行为,在默认情况下,将线路重定向到儿童状态。它不需要做url:“或$state.go(),它们不能正常工作。

So, in config file:

因此,在配置文件:

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

In state file:

在国家文件:

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

I've made an example on gist: https://gist.github.com/nikoloza/4ab3a94a3c6511bb1dac

我在gist上做了一个例子:https://gist.github.com/nikoloza/4ab3a94a3a3a3c6511bb1dac

#3


4  

You need to step back and think about what you're trying to achieve. What's the point of having a state when all it's doing is redirecting to a child state?

你需要退后一步,想一想你想要达到的目标。当一个州的所有工作都是针对一个儿童国家的时候,有什么意义呢?

Regarding your first question, parent states are always activated when you arrive at a child state, this behaviour is extremely useful in sharing data among states, and without it nested routing would be impossible (or rather wouldn't make sense).

关于您的第一个问题,当您到达子状态时,父状态总是被激活,这种行为对于在状态之间共享数据非常有用,如果没有它,嵌套路由将是不可能的(或者更确切地说没有意义)。

As for the 2nd question, I've worked on a few big angular apps and so far I haven't found myself needing to do that.

至于第二个问题,我已经开发了一些大的有棱角的应用程序,到目前为止我还没有发现自己需要这么做。

OK, believe it or not, as much I'd hate to say it, right now I came across a scenario where I needed to do this. I have a profile/userName route (technically this should be profile/userName/details) and a profile/userName/products route, I wanted to have a master view for both states but at the same time I wanted the profile/userName route have a clean url, like: profile/mike62, NOT profile/mike62/details. So I ended up doing this:

好吧,信不信由你,尽管我不愿意这么说,但现在我遇到了一个我需要这样做的场景。我有一个配置文件/用户名路径(技术上应该是配置文件/用户名/详细信息)和一个配置文件/用户名/产品路径,我想要两个状态都有一个主视图,但同时我希望配置文件/用户名路径有一个干净的url,比如:配置文件/mike62,而不是配置文件/mike62/详细信息。最后我这样做了:

.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:/?.*}'})

Ended up achieving it like this, there are many ways though:

最终达到这个目标的方法有很多:

in my publicProfile state controller (this is the base state):

在我的publicProfile state controller(这是基状态):

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

Yes, it does feel hacky but now I know that there are some edge cases where we want to do this, althopugh we could rethink our state design altogether. Another, dirtier way would be to check the current state in a $timeout with a small delay inside the base state, if we are not on the publicProfile.products state, we navigate to our preferred/de-facto state of publicProfile.details.

是的,这确实让人觉得很陈腐,但现在我知道有一些边缘案例我们想这样做,但是我们可以重新考虑我们的国家设计。另一种更脏的方法是在$timeout中检查当前状态,如果不在publicProfile上,则在base状态中有一个小的延迟。产品状态,我们导航到首选的/事实上的publicProfile.details状态。

#4


3  

It looks like you're trying to set a default child state.

看起来您正在尝试设置一个默认的子状态。

That's a commonly asked question about ui-router: How to: Set up a default/index child state

这是关于ui-router的一个常见问题:如何:设置默认/索引子状态

The tl;dr is to use abstract states by settings abstract: true in the parent state. If you add multiple child states, it'll default to first child state.

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


2  

Per pdvorchik's answer on the related GitHub thread, there is a simple fix for this which worked perfectly for me, consisting of wrapping the $state.go call in a .finally() call to make sure the first transition completes before a new one is started.

在相关的GitHub线程上,根据pdvorchik的回答,有一个简单的修复方法,对我来说非常有用,包括包装$state。在.finally()调用中进行调用,以确保在启动新转换之前完成第一个转换。

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

#6


1  

After @Chris T's explanation, it seems that the cleanest solution is to listen for the $stateChangeSuccess event:

在@Chris T的解释之后,似乎最干净的解决方案是监听$stateChangeSuccess事件:

redirects =
  inventory: 'inventory.low'

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

Where redirects will contain any route redirects that may occur. Thanks all!

重定向将包含可能发生的任何路由重定向。感谢所有!

#7


0  

Don't make the low inventory state a child state.

不要将低库存状态设置为子状态。

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

#1


19  

1) Why is the parent state being reinitialized when .go is called on a child state?

1)为什么在子状态上调用.go时要重新初始化父状态?

While a transition is in process, any $state.go/transitionTo will cause the currently in process transition to be Superseded. An in-process transition that is superseded is cancelled immediately. Since your original transition to inventory is not completed by the time all the states' onEnters are called, the original transition is cancelled and a new transition to inventory.low is started from the previously active state.

当转换正在进行时,任何$状态。go/transitionTo会导致当前正在进行的转换被取代。被取代的进程内转换立即被取消。由于您最初向库存的转换在调用所有状态的承销商时还没有完成,因此取消了最初的转换,并重新转换到库存。low是从先前的活动状态开始的。

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

参见ui-router src https://github.com/angular-ui/ui-router/blob/master/src/state.js#L897…

2) How then, would you handle redirecting to a child state?

2)如何处理重定向到子状态?

You could...

你可以…

  • Wrap $state.go in a $timeout() to allow the original transition to complete before redirecting.
  • 包装状态。进入$timeout()允许在重定向之前完成原始转换。
  • Call $state.go from your controller instead. UI-router invokes the controller from the ui-view directive AFTER the transition is completed.
  • 称美元的状态。从控制器开始。在转换完成后,UI-router从ui-view指令调用控制器。

In any case, be very sure you want your app to redirect like this. If a user navigated directly to any other child state of inventory (such as inventory.high), the redirect will still occur, forcing them to inventory.low which would not be what they intended.

无论如何,一定要让你的应用像这样重定向。如果用户直接导航到任何其他的子目录(如inventory.high),那么重定向仍然会发生,迫使它们进行编目。低,这不是他们想要的。

#2


9  

I had the same problem. The simple solution I found is to listen state changes and redirect to your child state from there.

我也有同样的问题。我找到的一个简单的解决方案是监听状态的变化并从那里重定向到您的子状态。

It's kind of hack that makes routes redirecting to child states by default. It's not needed to do url: '' or $state.go(), they don't work correctly.

这是一种黑客行为,在默认情况下,将线路重定向到儿童状态。它不需要做url:“或$state.go(),它们不能正常工作。

So, in config file:

因此,在配置文件:

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

In state file:

在国家文件:

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

I've made an example on gist: https://gist.github.com/nikoloza/4ab3a94a3c6511bb1dac

我在gist上做了一个例子:https://gist.github.com/nikoloza/4ab3a94a3a3a3c6511bb1dac

#3


4  

You need to step back and think about what you're trying to achieve. What's the point of having a state when all it's doing is redirecting to a child state?

你需要退后一步,想一想你想要达到的目标。当一个州的所有工作都是针对一个儿童国家的时候,有什么意义呢?

Regarding your first question, parent states are always activated when you arrive at a child state, this behaviour is extremely useful in sharing data among states, and without it nested routing would be impossible (or rather wouldn't make sense).

关于您的第一个问题,当您到达子状态时,父状态总是被激活,这种行为对于在状态之间共享数据非常有用,如果没有它,嵌套路由将是不可能的(或者更确切地说没有意义)。

As for the 2nd question, I've worked on a few big angular apps and so far I haven't found myself needing to do that.

至于第二个问题,我已经开发了一些大的有棱角的应用程序,到目前为止我还没有发现自己需要这么做。

OK, believe it or not, as much I'd hate to say it, right now I came across a scenario where I needed to do this. I have a profile/userName route (technically this should be profile/userName/details) and a profile/userName/products route, I wanted to have a master view for both states but at the same time I wanted the profile/userName route have a clean url, like: profile/mike62, NOT profile/mike62/details. So I ended up doing this:

好吧,信不信由你,尽管我不愿意这么说,但现在我遇到了一个我需要这样做的场景。我有一个配置文件/用户名路径(技术上应该是配置文件/用户名/详细信息)和一个配置文件/用户名/产品路径,我想要两个状态都有一个主视图,但同时我希望配置文件/用户名路径有一个干净的url,比如:配置文件/mike62,而不是配置文件/mike62/详细信息。最后我这样做了:

.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:/?.*}'})

Ended up achieving it like this, there are many ways though:

最终达到这个目标的方法有很多:

in my publicProfile state controller (this is the base state):

在我的publicProfile state controller(这是基状态):

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

Yes, it does feel hacky but now I know that there are some edge cases where we want to do this, althopugh we could rethink our state design altogether. Another, dirtier way would be to check the current state in a $timeout with a small delay inside the base state, if we are not on the publicProfile.products state, we navigate to our preferred/de-facto state of publicProfile.details.

是的,这确实让人觉得很陈腐,但现在我知道有一些边缘案例我们想这样做,但是我们可以重新考虑我们的国家设计。另一种更脏的方法是在$timeout中检查当前状态,如果不在publicProfile上,则在base状态中有一个小的延迟。产品状态,我们导航到首选的/事实上的publicProfile.details状态。

#4


3  

It looks like you're trying to set a default child state.

看起来您正在尝试设置一个默认的子状态。

That's a commonly asked question about ui-router: How to: Set up a default/index child state

这是关于ui-router的一个常见问题:如何:设置默认/索引子状态

The tl;dr is to use abstract states by settings abstract: true in the parent state. If you add multiple child states, it'll default to first child state.

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


2  

Per pdvorchik's answer on the related GitHub thread, there is a simple fix for this which worked perfectly for me, consisting of wrapping the $state.go call in a .finally() call to make sure the first transition completes before a new one is started.

在相关的GitHub线程上,根据pdvorchik的回答,有一个简单的修复方法,对我来说非常有用,包括包装$state。在.finally()调用中进行调用,以确保在启动新转换之前完成第一个转换。

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

#6


1  

After @Chris T's explanation, it seems that the cleanest solution is to listen for the $stateChangeSuccess event:

在@Chris T的解释之后,似乎最干净的解决方案是监听$stateChangeSuccess事件:

redirects =
  inventory: 'inventory.low'

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

Where redirects will contain any route redirects that may occur. Thanks all!

重定向将包含可能发生的任何路由重定向。感谢所有!

#7


0  

Don't make the low inventory state a child state.

不要将低库存状态设置为子状态。

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