当用户使用 UI-Router 转换到其父状态时将用户定向到子状态
Posted
技术标签:
【中文标题】当用户使用 UI-Router 转换到其父状态时将用户定向到子状态【英文标题】:Directing the user to a child state when they are transitioning to its parent state using UI-Router 【发布时间】:2013-06-04 19:01:52 【问题描述】:考虑以下几点:
.state('manager.staffList', url:'^/staff?alpha', templateUrl: 'views/staff.list.html', data:activeMenu: 'staff', controller: 'staffListCtrl')
.state('manager.staffDetail', url:'^/staff/id' , templateUrl: 'views/staff.html', data:activeMenu: 'staff', controller: 'staffDetailsCtrl')
.state('manager.staffDetail.view', url:'/view', templateUrl: 'views/staff.details.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.schedule', url:'/schedule', templateUrl:'views/staff.view.schedule.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.history', url:'/history' , templateUrl:'views/staff.view.history.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.log', url:'/log', templateUrl:'views/staff.view.log.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.files', url:'/files', templateUrl:'views/staff.view.files.html', data:activeMenu: 'staff')
.state('manager.staffDetail.edit', url:'/edit', templateUrl: 'views/staff.edit.html', data:activeMenu: 'staff')
如果我转到domain.com/staff/1234/view
,如何默认为manager.staffDetail.view.schedule
子状态?
【问题讨论】:
仅供参考,我目前使用的redirectTo
方法在这里找到 ***.com/questions/29491079/…
请不要编辑它来创建更多垂直代码。代码经过专门格式化,以直观地显示子状态与其父状态的比较。
@UdayHiwarale 这是上面已经发布的同一个链接
我的错。我没看到。
【参考方案1】:
首先,将属性添加到 abstract:true
的'manager.staffDetail.view'
状态。这不是必需的,但您想设置它,因为您永远不会直接进入此状态,您总是会进入其中一个子状态。
然后执行以下一项操作:
为'manager.staffDetail.view.schedule'
状态提供一个空 URL。这将使它与父状态 url 匹配相同的 url,因为它不会向父 url 附加任何内容。
.state('manager.staffDetail.view.schedule', url:'', ...
或者如果你想保持默认子路由的url不变,那么在你的module.config中设置一个redirect。这里的代码会将'/staff/id/view'
的任何位置重定向到'/staff/id/view/schedule'
的位置:
$urlRouterProvider.when('/staff/id/view', '/staff/id/view/schedule');
【讨论】:
对于$urlRouterProvider
重定向,在使用带有ui-sref="manager.staffDetail.view"
的链接时出现错误,因此我需要从父状态声明中删除abstract: true
。
第二个选项不适用于内部 ui-sref 链接(它不会转到子状态)【参考方案2】:
要设置默认子视图,请查看example。点击Route 1
时加载默认状态route1.list
// For any unmatched url, send to /route1
$stateProvider
.state('route1',
url: "/route1",
abstract:true ,
templateUrl: "route1.html"
)
.state('route1.list',
url: '',
templateUrl: "route1.list.html",
controller: function($scope)
$scope.items = ["A", "List", "Of", "Items"];
)
.state('route1.desc',
url: "/desc",
templateUrl: "route1.desc.html",
controller: function($scope)
$scope.items = [];
)
.state('route2',
url: "/route2",
templateUrl: "route2.html"
)
.state('route2.list',
url: "/list",
templateUrl: "route2.list.html",
controller: function($scope)
$scope.things = ["A", "Set", "Of", "Things"];
)
【讨论】:
如果route1.desc
没有url,你仍然可以使用$state.go
访问它,但route1.list
将默认加载到/route1
:) 如果你不想要这很有用通过 url 直接访问状态
如果我们想在 ui-sref 中使用父状态,这有一个很大的问题
你必须使用 href="/abstractroute" 链接到父节点
谁能向我解释为什么这个例子中的route1
状态需要一个templateUrl?或者更一般地说,为什么abstract:true
状态需要模板?
孩子的父母应该有abstract:true
的那个【参考方案3】:
我遇到了同样的问题,发现这个解决方案可行:
https://github.com/angular-ui/ui-router/issues/948#issuecomment-75342784
这是引用自 github 上的@christopherthielen
“现在,不要声明你的状态摘要,并使用这个配方:”
app.run($rootScope, $state)
$rootScope.$on('$stateChangeStart', function(evt, to, params)
if (to.redirectTo)
evt.preventDefault();
$state.go(to.redirectTo, params)
);
$stateProvider.state('parent' ,
url: "/parent",
templateUrl: "parent.html",
redirectTo: 'parent.child'
);
$stateProvider.state('parent.child' ,
url: "/child",
templateUrl: "child.html"
);
以下是流程工作原理的细分:
-
用户导航到“父”状态
“$stateChangeStart”事件被触发
“$stateChangeStart”的侦听器捕获事件并将“toState”(即“父”)和“事件”传递给处理函数
处理函数检查是否在“toState”上设置了“redirectTo”。
如果未设置“redirectTo”,则不会发生任何事情,用户会继续进入“toState”状态。
如果设置了“redirectTo”,则取消事件 (event.preventDefault) 并且 $state.go(toState.redirectTo) 将它们发送到“redirectTo”中指定的状态(即“parent.child”)。
“$stateChangeStart”事件再次触发,但这次“toState”==“parent.child”且“redirectTo”选项未设置,因此它继续“toState”。
【讨论】:
谁能解释一下。这不会变成无限循环吗? 嗨 Kishore - 我更新了答案以解释它是如何工作的。除非您在“redirectTo”选项中明确指定“父”状态,否则不会出现无限循环。 我试过这个,但不幸的是这对我不起作用。我正在使用 ui-router v0.3.1【参考方案4】:很晚了,但我认为您可以“重定向”到您想要的状态。
.state('manager.staffDetail.view',
url:'/view',
templateUrl: 'views/staff.details.html',
controller: 'YourController'
)
app.controller('YourController', ['$state',
function($state)
$state.go('manager.staffDetail.view.schedule');
]);
您可以直接在状态配置中编写控制器。
【讨论】:
我试过了,但它会加倍加载控制器、解析和任何 onStateChange。我只使用了 $urlRouterProvider.when('/app/service', '/app/service/list') 只会加载两次或无限次,因为子状态还会再次调用父状态,因为我们再次重定向到子状态。 如果您的导航有父 sref,它将无法在后续点击中起作用 - 仅在初始控制器加载时起作用。【参考方案5】:我将“manager.staffDetial.view”更改为抽象状态,并将默认子状态的 url 留空“”
// Staff
.state('manager.staffList', url:'^/staff?alpha', templateUrl: 'views/staff.list.html', data:activeMenu: 'staff', controller: 'staffListCtrl')
.state('manager.staffDetail', url:'^/staff/id', templateUrl: 'views/staff.html', data:activeMenu: 'staff', controller: 'staffDetailsCtrl')
.state('manager.staffDetail.view', url:'/view', abstract: true, templateUrl: 'views/staff.details.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.schedule', url:'', templateUrl:'views/staff.view.schedule.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.history', url:'/history', templateUrl:'views/staff.view.history.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.log', url:'/log', templateUrl:'views/staff.view.log.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.files', url:'/files', templateUrl:'views/staff.view.files.html', data:activeMenu: 'staff')
.state('manager.staffDetail.edit', url:'/edit', templateUrl: 'views/staff.edit.html', data:activeMenu: 'staff')
【讨论】:
所以你基本上完全按照我的建议做了......为什么不给我的答案打上正确的复选标记呢?这样我就可以获得代表点数。 我不确定你知道“确切”这个词是什么意思。但是,您的代码示例使用了 routeProvider 方法。我选择根本不使用它,而是将解决方案保留在状态管理器中。这样代码保持干净,不会添加任何不必要的额外代码行。您的示例是一个可能的答案,但不是最可接受的答案。 似乎你所做的一切,我都提到过(虽然,是的,我的示例中的代码是不必要的)。我:“……你应该把它设置为抽象状态。”你:“我将 'manager.staffDetial.view' 更改为抽象状态......” 我:“或者不同的选择是给日程安排一个空的 URL。”你:“......并将我的默认子状态的 url 留空”。不过,真的没有难过的感觉,我很高兴你明白了。 抱歉,您的回答令人困惑,而且不准确。在阅读了您的解释后,我看到您给出了单独的选项,最后如果我将您的第一个和最后一个选项结合起来并省略中间的建议,我会有一个解决方案。我想我知道你要去哪里,但如果我将你的答案标记为答案,我不确定它是否会对下一位读者有所帮助。如果您想编辑您的答案并澄清您的答案,我很乐意为您提供答案。 是的,你是对的,我用手机写了答案......:P 我已经清理了答案,我希望它现在更有意义。我还注意到,如果我在 Google 上搜索 ui-router,则此线程是第四个结果,所以 重要的是我们必须非常清楚。【参考方案6】:在“angular-ui-router”:“0.2.13”中,我认为@nfiniteloop 的重定向解决方案不会起作用。一旦我回滚到 0.2.12,它就起作用了(并且可能不得不将 $urlRouterProvider.when 调用放在 $stateProvider 之前?)
见https://***.com/a/26285671/1098564
和https://***.com/a/27131114/1098564 寻求解决方法(如果您不想回到 0.2.12)
截至 2014 年 11 月 28 日,https://github.com/angular-ui/ui-router/issues/1584 表示它应该在 0.2.14 中再次工作
【讨论】:
【参考方案7】:这已经很晚了,但这里的所有答案都不适用于 AngularJS 的 angular-ui-router 的最新版本。从那个版本开始(特别是@uirouter/angularjs#v1.0.x,你可以把redirectTo: 'childStateName'
放在$stateProvider.state()
的第二个参数中。例如:
$stateProvider
.state('parent',
resolve: ...,
redirectTo: 'parent.defaultChild'
)
.state('parent.defaultChild', ...)
这是相关的文档部分:https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-hook-redirectto
希望这对某人有所帮助!
【讨论】:
【参考方案8】:这是一个非常简单和透明的替代方案,只需在 ui 路由器中修改父状态:
.state('parent_state',
url: '/something/:param1/:param2',
templateUrl: 'partials/something/parent_view.html', // <- important
controller: function($state, $stateParams)
var params = angular.copy($state.params);
if (params['param3'] === undefined)
params['param3'] = 'default_param3';
$state.go('parent_state.child', params)
)
.state('parent_state.child',
url: '/something/:param1/:param2/:param3',
templateUrl: '....',
controller: '....'
)
【讨论】:
【参考方案9】:添加angular-ui-router-default 并将abstract
和default
选项添加到父状态:
...
.state('manager.staffDetail.view', abstract: true, default: '.schedule', url:'/view', templateUrl: 'views/staff.details.html', data:activeMenu: 'staff')
.state('manager.staffDetail.view.schedule', url:'/schedule', templateUrl:'views/staff.view.schedule.html', data:activeMenu: 'staff')
...
注意:要使其正常工作,父模板中必须有<ui-view/>
。
【讨论】:
以上是关于当用户使用 UI-Router 转换到其父状态时将用户定向到子状态的主要内容,如果未能解决你的问题,请参考以下文章
如何在触发状态转换 ui-router 时保留 ui-view 的旧内容并更改另一个
当 UI-Router ES6 中的状态更改时,$stateChangeStart 不会被触发?
使用 AngularJS 中的 UI-Router 将状态重定向到默认子状态