Ember技术问题:如何从父路由获取活动子路由名称
在EmberJS父控制器中获取活跃子路由名称的几种方法
嘿,这个需求我之前做项目时也碰到过,要在父控制器里拿到当前活跃的子路由名称来实现复杂的属性绑定,其实有几种很实用的方案,我给你详细说说:
方法一:利用router.currentRouteName计算属性
这是最直接的方式,通过应用的路由服务获取当前完整路由名,再提取子路由部分:
// 父控制器代码 import Controller from '@ember/controller'; import { computed } from '@ember/object'; export default class ParentController extends Controller { @computed('router.currentRouteName') get activeChildRoute() { const currentRoute = this.get('router.currentRouteName'); const parentRouteName = this.get('routeName'); // 父路由自身的名称,比如'parent' if (currentRoute && currentRoute.startsWith(`${parentRouteName}.`)) { // 拆分路由名,提取子路由部分 return currentRoute.split('.')[1]; } return null; } }
这个方案的优点是代码简洁,不需要额外监听事件;如果你的子路由是单层嵌套,完全够用。要是有多层嵌套(比如parent.child.grandchild),可以调整字符串拆分的逻辑,比如取最后一段或者指定位置的片段。
方法二:监听路由didTransition事件
如果需要在路由切换前后做额外逻辑,或者想要更精准地控制属性更新时机,可以在父路由里监听全局的路由过渡完成事件,再把活跃子路由名传递给父控制器:
// 父路由代码 import Route from '@ember/routing/route'; export default class ParentRoute extends Route { setupController(controller) { super.setupController(...arguments); // 监听路由过渡完成事件 const updateActiveChild = () => { const currentRouteName = this.router.currentRouteName; const parentName = this.routeName; let activeChild = null; if (currentRouteName.startsWith(`${parentName}.`)) { activeChild = currentRouteName.split('.')[1]; } controller.set('activeChildRoute', activeChild); }; this.router.on('didTransition', updateActiveChild); // 记得在路由销毁时移除监听,避免内存泄漏 this.router.one('willDestroy', () => { this.router.off('didTransition', updateActiveChild); }); } }
然后在父控制器里直接使用activeChildRoute属性就可以了,这种方式更灵活,适合需要配合路由过渡做其他操作的场景。
方法三:通过路由实例匹配子路由
如果你的路由嵌套结构比较复杂,想要直接通过路由实例来判断活跃子路由,可以利用父路由的children属性和当前路由实例做匹配:
// 父控制器代码 import Controller from '@ember/controller'; import { computed } from '@ember/object'; export default class ParentController extends Controller { @computed('router.currentRoute') get activeChildRoute() { const currentRoute = this.get('router.currentRoute'); const parentRoute = this.get('route'); // 获取父路由实例 // 遍历父路由的子路由,匹配当前路由的祖先关系 const activeChild = parentRoute.children.find(childRoute => { return currentRoute.name.startsWith(`${parentRoute.routeName}.${childRoute.routeName}`); }); return activeChild ? activeChild.routeName : null; } }
这个方法更面向路由实例,能准确识别对应的子路由,哪怕子路由还有更深的嵌套,也能匹配到最上层的子路由名称。
Ember Octane 适配提示
如果你用的是Ember Octane及以上版本,推荐使用@tracked和原生类语法来实现,代码更简洁:
// Octane 风格的父控制器 import Controller from '@ember/controller'; import { tracked } from '@glimmer/tracking'; import { inject } from '@ember/service'; export default class ParentController extends Controller { @inject router; @tracked activeChildRoute; constructor() { super(...arguments); const updateActiveChild = () => { const currentRoute = this.router.currentRouteName; const parentName = this.routeName; if (currentRoute.startsWith(`${parentName}.`)) { this.activeChildRoute = currentRoute.split('.')[1]; } else { this.activeChildRoute = null; } }; this.router.on('didTransition', updateActiveChild); this.router.one('willDestroy', () => { this.router.off('didTransition', updateActiveChild); }); } }
根据你的具体场景选对应的方法就好啦,要是只是简单的子路由判断,第一种方法最省心;复杂场景的话,后两种方案更合适。
内容的提问来源于stack exchange,提问作者Sean Mackesey
相关产品推荐
相关产品推荐

