【问题标题】:Angular JS ui-router how to redirect to a child state from a parent?Angular JS ui-router如何从父母重定向到子状态?
【发布时间】:2014-07-25 16:26:06
【问题描述】:

当使用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'

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

  1. 为什么在子状态上调用 .go 时会重新初始化父状态?
  2. 那么,您将如何处理重定向到子状态?

【问题讨论】:

  • TaylorMac,看我的编辑。

标签: javascript angularjs angular-ui-router


【解决方案1】:

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

当一个过渡正在进行时,任何 $state.go/transitionTo 都会导致 currently in process 过渡被取代。被取代的进程内转换将立即取消。由于在调用所有状态的 onEnter 时尚未完成到 inventory 的原始转换,因此将取消原始转换并从先前的活动状态开始到 inventory.lownew 转换。

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

2) 那么,您将如何处理重定向到子状态?

你可以……

  • $state.go 包裹在$timeout() 中,以便在重定向之前完成原始转换。
  • 改为从您的控制器调用 $state.go。 UI-router 在转换完成后从 ui-view 指令调用控制器。

无论如何,请确保您希望您的应用像这样重定向。如果用户直接导航到库存的任何其他子状态(例如 inventory.high),重定向仍然会发生,迫使他们转到 inventory.low,这不是他们想要的。

【讨论】:

  • 对于 1),很好 - 回答了这个问题。不是因为它重定向到子状态而重新初始化(这是我认为很奇怪的),而是因为状态转换未完成。关于在子状态中过渡到 .low 的好处 - 我什至没有考虑过。也许在“库存”上收听 $stateChangeStart 是最好的方法。赞一个!
  • 另见我的宠物项目,ui-router-extras。它有一个名为 DeepStateRedirect 的功能,可以做你想做的事。它确实在监听 $stateChangeStart,例如,'inventory':christopherthielen.github.io/ui-router-extras/example/dsr/…
  • 除了查看 $stateChangeStart 之外,您还可以在控制器中使用 if 检查 $state.current.name 来执行此操作,因此重定向仅在您处于父状态时发生。
  • 或者您可以在父状态对象上定义redirectTo 属性,将其重定向到子状态。这应该会确保在重定向之前完成父状态。请参阅ui-router's own examplehttps://github.com/ui-router/sample-app/blob/master/app/app.state.js 中的示例。 let appState = { name: 'app', redirectTo: 'welcome', template: template, controller: controller, controllerAs: 'vm' };
【解决方案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,不是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

【讨论】:

  • 我对此的想法是:1)你为什么不直接响应 $stateChangeStart 事件,而不是让模型可观察?我了解在模板中使用 $state 来增加 $rootScope 的用处,但是观察模板上的属性并将其与状态相关联?我不知道这是否比放入至少本地化的控制器好得多
  • 您描述的场景确实需要这个,并且在您考虑分层模块化时非常有用
【解决方案4】:

您似乎正在尝试设置默认子状态。

这是一个关于ui-router的常见问题:How to: Set up a default/index child state

tl;dr 是通过在父状态下设置abstract: true 来使用abstract states。如果添加多个子状态,它将默认为第一个子状态。

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

【讨论】:

  • 嘿@Atav32 这仍然有效吗?我尝试按照常见问题解答所说的做,但在单击我的 ui-sref="parent" 后仍然收到“错误:无法转换到抽象状态 'parent'”。
  • @Exegesis 嗯,我还没有尝试链接到父状态。我只是直接链接到“默认”孩子。 ui-sref="parent.child"
  • 哇,这似乎是正确的方法!不错的参考,也是。我唯一担心的是使用 gulp,不知道谁将成为第一个子状态.. 对此有什么想法吗?
  • @Z.Khullah 你能解释一下 Gulp 会如何干扰吗?
  • 找到了,根据参考你可以使用 empty URL 来设置默认是谁,或者在discussions中找到你可以使用 redirectTo 自动定义+重定向。最后一个还解决了@Exegesis 问题,在this specific thread
【解决方案5】:

根据 pdvorchik 在related GitHub thread 上的回答,有一个简单的解决方法对我来说非常有效,包括将$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
    

    重定向将包含可能发生的任何路由重定向。谢谢大家!

    【讨论】:

    • 就我个人而言,我会把它挂在 $stateChangeStart 中,如果有重定向,在执行重定向之前运行 e.preventDefault() 以取消转换。
    • 是的,我同意,这就是我实现的
    【解决方案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'
    

    【讨论】:

    • 对,但是它不会解析父状态映射对象的promise,也不会继承数据,也不会加载到库存状态模板的ui-view中,即整点
    猜你喜欢
    • 1970-01-01
    • 2015-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多