Javascript 如何使用AngularJS+;角度用户界面路由器
web应用程序的一个关键组件是面包屑/导航。使用Angular UI Router,将面包屑元数据放在各个状态中而不是放在控制器中是有意义的。为每个需要的控制器手动创建breadcrumbs对象是一项简单的任务,但也是一项非常复杂的任务 我已经看到了一些自动面包屑角的解决方案,但老实说,它们相当原始。有些状态,如对话框或侧面板,不应更新面包屑,但如果当前的插件为angular,则无法表达这一点 另一个问题是面包屑的标题不是静态的。例如,如果转到用户详细信息页面,面包屑标题可能应该是用户的全名,而不是通用的“用户详细信息” 最后一个需要解决的问题是为父链接使用所有正确的状态参数值。例如,如果您正在查看一家公司的用户详细信息页面,显然您希望知道父状态需要一个Javascript 如何使用AngularJS+;角度用户界面路由器,javascript,angularjs,angular-ui,breadcrumbs,Javascript,Angularjs,Angular Ui,Breadcrumbs,web应用程序的一个关键组件是面包屑/导航。使用Angular UI Router,将面包屑元数据放在各个状态中而不是放在控制器中是有意义的。为每个需要的控制器手动创建breadcrumbs对象是一项简单的任务,但也是一项非常复杂的任务 我已经看到了一些自动面包屑角的解决方案,但老实说,它们相当原始。有些状态,如对话框或侧面板,不应更新面包屑,但如果当前的插件为angular,则无法表达这一点 另一个问题是面包屑的标题不是静态的。例如,如果转到用户详细信息页面,面包屑标题可能应该是用户的全名,而
:companyId
angular是否有任何插件提供这种级别的面包屑支持?如果没有,最好的方法是什么?我不想把我的控制器弄得乱七八糟——我会有很多这样的控制器——我想让它尽可能自动化、无痛
谢谢 我不相信有内置的功能,但所有的工具都为您准备好了,请看一下。您可以简单地让导航元素使用它,而您想知道的任何其他内容都只需注入它即可
深入研究ui路由器的内部结构后,我明白了如何使用解析的资源创建面包屑
注意:我无法使此代码在plunker中正常工作,但该指令在我的项目中有效。routes.js仅提供了如何为面包屑设置标题的示例。不久前我自己解决了这个问题,因为没有可用的标题。我决定不使用
data
对象,因为我们实际上不希望面包屑标题被孩子继承。有时会出现模式对话框和右面板,从技术上讲是“儿童视图”,但它们不应影响面包屑。通过使用breadcrumb
对象,我们可以避免自动继承
对于实际的title
属性,我使用$interpolate
。我们可以将面包屑数据与resolve作用域相结合,而无需在其他位置执行resolve。在我遇到的所有情况下,我只想使用resolve作用域,所以这非常有效
我的解决方案也可以处理i18n
$stateProvider
.state('courses', {
url: '/courses',
template: Templates.viewsContainer(),
controller: function(Translation) {
Translation.load('courses');
},
breadcrumb: {
title: 'COURSES.TITLE'
}
})
.state('courses.list', {
url: "/list",
templateUrl: 'app/courses/courses.list.html',
resolve: {
coursesData: function(Model) {
return Model.getAll('/courses');
}
},
controller: 'CoursesController'
})
// this child is just a slide-out view to add/edit the selected course.
// It should not add to the breadcrumb - it's technically the same screen.
.state('courses.list.edit', {
url: "/:courseId/edit",
templateUrl: 'app/courses/courses.list.edit.html',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne("/courses", $stateParams.courseId);
}
},
controller: 'CourseFormController'
})
// this is a brand new screen, so it should change the breadcrumb
.state('courses.detail', {
url: '/:courseId',
templateUrl: 'app/courses/courses.detail.html',
controller: 'CourseDetailController',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne('/courses', $stateParams.courseId);
}
},
breadcrumb: {
title: '{{course.name}}'
}
})
// lots more screens.
我不想将面包屑与指令绑定,因为我认为在我的应用程序中可能有多种方式可以直观地显示面包屑。因此,我将其投入服务:
.factory("Breadcrumbs", function($state, $translate, $interpolate) {
var list = [], title;
function getProperty(object, path) {
function index(obj, i) {
return obj[i];
}
return path.split('.').reduce(index, object);
}
function addBreadcrumb(title, state) {
list.push({
title: title,
state: state
});
}
function generateBreadcrumbs(state) {
if(angular.isDefined(state.parent)) {
generateBreadcrumbs(state.parent);
}
if(angular.isDefined(state.breadcrumb)) {
if(angular.isDefined(state.breadcrumb.title)) {
addBreadcrumb($interpolate(state.breadcrumb.title)(state.locals.globals), state.name);
}
}
}
function appendTitle(translation, index) {
var title = translation;
if(index < list.length - 1) {
title += ' > ';
}
return title;
}
function generateTitle() {
title = '';
angular.forEach(list, function(breadcrumb, index) {
$translate(breadcrumb.title).then(
function(translation) {
title += appendTitle(translation, index);
}, function(translation) {
title += appendTitle(translation, index);
}
);
});
}
return {
generate: function() {
list = [];
generateBreadcrumbs($state.$current);
generateTitle();
},
title: function() {
return title;
},
list: function() {
return list;
}
};
})
以及模板:
<h2 translate-cloak>
<ul class="breadcrumbs">
<li ng-repeat="breadcrumb in Breadcrumbs.list()">
<a ng-if="breadcrumb.state && !$last" ui-sref="{{breadcrumb.state}}">{{breadcrumb.title | translate}}</a>
<span class="active" ng-show="$last">{{breadcrumb.title | translate}}</span>
<span ng-hide="$last" class="divider"></span>
</li>
</ul>
</h2>
-
{{breadcrumb.title | translate}}
{{breadcrumb.title | translate}}
从这里的屏幕截图中,您可以看到它在以下两种导航中都能完美工作:
以及html
标记:
对Angular UI团队的提示:请在开箱即用的情况下添加类似的内容 感谢@egervari提供的解决方案。对于那些需要在面包屑的自定义数据中添加一些$stateParams属性的用户。我已经为键'title'的值扩展了语法{:id}
.state('courses.detail', {
url: '/:courseId',
templateUrl: 'app/courses/courses.detail.html',
controller: 'CourseDetailController',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne('/courses', $stateParams.courseId);
}
},
breadcrumb: {
title: 'course {:courseId}'
}
})
这里有一个例子。仅供参考。我想分享我的解决方案。它的优点是不需要向控制器中注入任何内容,并支持命名的面包屑标签,以及使用
resolve:
函数命名面包屑
状态配置示例:
$stateProvider
.state('home', {
url: '/',
...
data: {
displayName: 'Home'
}
})
.state('home.usersList', {
url: 'users/',
...
data: {
displayName: 'Users'
}
})
.state('home.userList.detail', {
url: ':id',
...
data: {
displayName: '{{ user.name | uppercase }}'
}
resolve: {
user : function($stateParams, userService) {
return userService.getUser($stateParams.id);
}
}
})
然后,您需要在指令的属性中指定面包屑标签(displayname)的位置:
<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>
通过这种方式,指令将知道查看$state.$current.data.displayName
的值以查找要使用的文本
$interpolate-able面包屑名称
请注意,在上一个状态(home.userList.detail
)中,displayName使用通常的角度插值语法{{value}
。这允许您引用状态配置中resolve
对象中定义的任何值。通常,这将用于从服务器获取数据,如上面的用户名示例所示。请注意,因为这只是一个规则的角度字符串,所以可以在displayName字段中包含任何类型的有效角度表达式,就像上面的示例中我们对其应用过滤器一样
演示
以下是Plunker的工作演示:
代码
我觉得把所有的代码放在这里有点太多了,所以在GitHub上:我制作了一个角度模块,它根据ui路由器的状态生成面包屑。您提到的所有功能都包括在内(我最近添加了在阅读本文时忽略面包屑中状态的可能性:-): 这是 它允许根据控制器范围内插动态标签(嵌套/多视图情况下的“最深”) 状态链可通过状态选项进行自定义(请参见)
该模块带有预定义的模板,并允许用户定义模板。基本上没有相关文档。我也使用Angular UI路由器,而不是Angular JS提供的ngRoute。为您添加了文档链接。同样通过Angular UI Router查看,在他们的示例中,位置似乎与Router一起使用。谢谢你们的反馈。鉴于老年退休金计划的答案十分详尽,我同意这是一个软弱的答案。然而,我只是试图将OP导向一个不同的方向。他的解决方案
<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>