本文介绍了如何使用 AngularJS + Angular UI Router 制作自动动态面包屑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Web 应用程序的一个关键组件是面包屑/导航.使用 Angular UI Router,将面包屑元数据与各个状态一起放置是有意义的,而不是放在您的控制器中.为每个需要的控制器手动创建面包屑对象是一项简单的任务,但也是一项非常麻烦的任务.

One key component to web applications is breadcrumbs/navigation. With Angular UI Router, it would make sense to put the breadcrumb metadata with the individual states, rather than in your controllers. Manually creating the breadcrumbs object for each controller where it's needed is a straight-forward task, but it's also a very messy one.

我见过一些使用 Angular 实现自动面包屑导航的解决方案,但老实说,它们相当原始.某些状态,如对话框或侧面板不应该更新面包屑,但使用当前的 angular 插件,无法表达.

I have seen some solutions for automated Breadcrumbs with Angular, but to be honest, they are rather primitive. Some states, like dialog boxes or side panels should not update the breadcrumbs, but with current addons to angular, there is no way to express that.

另一个问题是面包屑的标题不是静态的.例如,如果您转到用户详细信息"页面,面包屑标题可能应该是用户的全名,而不是通用的用户详细信息".

Another problem is that titles of breadcrumbs are not static. For example, if you go to a User Detail page, the breadcrumb title should probably be the user's Full Name, and not a generic "User Detail".

最后一个需要解决的问题是为父链接使用所有正确的状态参数值.例如,如果您正在查看公司的用户详细信息页面,显然您想知道父状态需要 :companyId.

The last problem that needs to be solved is using all of the correct state parameter values for parent links. For example, if you're looking at a User detail page from a Company, obviously you'll want to know that the parent state requires a :companyId.

是否有任何 angular 插件提供这种级别的面包屑支持?如果没有,最好的方法是什么?我不想弄乱我的控制器 - 我会有很多控制器 - 我想让它尽可能自动化和轻松.

Are there any addons to angular that provide this level of breadcrumbs support? If not, what is the best way to go about it? I don't want to clutter up my controllers - I will have a lot of them - and I want to make it as automated and painless as possible.

谢谢!

推荐答案

我不久前自己解决了这个问题,因为没有可用的东西.我决定不使用 data 对象,因为我们实际上不希望我们的面包屑标题被孩子们继承.有时会出现模态对话框和右侧面板,它们在技术上是子视图",但它们不应该影响面包屑.通过使用 breadcrumb 对象,我们可以避免自动继承.

I did solve this myself awhile back, because nothing was available. I decided to not use the data object, because we don't actually want our breadcrumb titles to be inherited by children. Sometimes there are modal dialogs and right panels that slide in that are technically "children views", but they shouldn't affect the breadcrumb. By using a breadcrumb object instead, we can avoid the automatic inheritance.

对于实际的 title 属性,我使用了 $interpolate.我们可以将我们的面包屑数据与解析范围结合起来,而不必在不同的地方进行解析.在我遇到的所有情况下,我只是想使用解析范围,所以这非常有效.

For the actual title property, I am using $interpolate. We can combine our breadcrumb data with the resolve scope without having to do resolves in a different place. In all of the cases I had, I just wanted to use the resolve scope anyway, so this works very well.

我的解决方案也处理 i18n.

My solution also handles i18n too.

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

我不想将面包屑与指令联系起来,因为我认为可能有多种方法可以在我的应用程序中直观地显示面包屑.所以,我把它放到了一个服务中:

I didn't want to tie the breadcrumbs to a directive, because I thought there might be multiple ways of showing the breadcrumb visually in my application. So, I put it into a service:

.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;
        }
    };
})

实际的面包屑指令变得非常简单:

The actual breadcrumb directive then becomes very simple:

.directive("breadcrumbs", function() {
    return {
        restrict: 'E',
        replace: true,
        priority: 100,
        templateUrl: 'common/directives/breadcrumbs/breadcrumbs.html'
    };
});

和模板:

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

从这里的屏幕截图,您可以看到它在两个导航中都完美运行:

From the screenshot here, you can see it works perfectly in both the navigation:

以及 html 标签:

As well as the html <title> tag:

对 Angular UI 团队的 PS:请立即添加类似的内容!

PS to Angular UI Team: Please add something like this out of the box!

这篇关于如何使用 AngularJS + Angular UI Router 制作自动动态面包屑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-26 00:00
查看更多