如何使用AngularJS + Angular UI路由器制作自动动态面包屑

ege*_*ari 35 javascript breadcrumbs angularjs angular-ui

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

我已经看到了一些使用Angular的自动面包屑的解决方案,但说实话,它们相当原始.某些状态,如对话框或侧面板不应更新面包屑,但使用角度的当前插件,无法表达.

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

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

是否有角度的插件可以提供这种级别的面包屑支持?如果没有,最好的方法是什么?我不想让我的控制器混乱 - 我会有很多 - 我希望尽可能自动化和无痛.

谢谢!

ege*_*ari 41

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

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

我的解决方案也处理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.
Run Code Online (Sandbox Code Playgroud)

我不想将面包屑绑定到指令,因为我认为在我的应用程序中可能有多种方式可视化地显示面包屑.所以,我把它放到一个服务中:

.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;
        }
    };
})
Run Code Online (Sandbox Code Playgroud)

实际的breadcrumb指令变得非常简单:

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

和模板:

<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>
Run Code Online (Sandbox Code Playgroud)

从这里的屏幕截图中,您可以看到它在导航中都能很好地工作:

在此输入图像描述

以及html <title>标签:

在此输入图像描述

PS到Angular UI团队:请开箱即用!


Mic*_*ley 24

我想与大家分享我的解决方案.它的优点是不需要将任何东西注入控制器,并支持命名的痕迹痕迹标签,以及使用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);
            }
        }
    })
Run Code Online (Sandbox Code Playgroud)

然后,您需要在指令的属性中指定面包屑标签(displayname)的位置:

<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>
Run Code Online (Sandbox Code Playgroud)

通过这种方式,指令将知道查看$state.$current.data.displayName要查找要使用的文本的值.

$ interpolate-able breadcrumb名称

请注意,在最后一个state(home.userList.detail)中,displayName使用通常的Angular插值语法{{ value }}.这允许您引用resolve状态配置中对象中定义的任何值.通常,这将用于从服务器获取数据,如上面的用户名示例所示.请注意,由于这只是一个常规的Angular字符串,因此您可以在displayName字段中包含任何类型的有效Angular表达式 - 如上例中我们对其应用过滤器的情况.

演示

这是关于Plunker的工作演示:http://plnkr.co/edit/bBgdxgB91Z6323HLWCzF?p = preview

我认为将所有代码放在这里有点多,所以这里是GitHub:https://github.com/michaelbromley/angularUtils/tree/master/src/directives/uiBreadcrumbs