/Angular-UI-Breadcrumbs

Angular UI-Bredcrumbs is an Angular 1 directive that auto-generates breadcrumbs from UI-Router routes.

Primary LanguageJavaScriptMIT LicenseMIT

Angular UI-Breadcrumbs

Angular UI-Bredcrumbs is an Angular 1 directive that auto-generates breadcrumbs from UI-Router routes.

This repository has been forked from the original repo by @michaelbromley, which is no longer being maintained.

Demo

You can see a working demo at: https://plnkr.co/edit/O8wS9OpyPQkj5FxoJEgj?p=preview

Requirements

This directive is designed to work with angular-ui-router, and will not work with the default Angular router.

The design of the directive also relies on the use of nested states in order to auto-generate the breadcrumbs hierarchy. See more on nested states here: https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#inherited-custom-data Note that the use of nested states does not imply nested views. Often, in a usual breadcrumbs use case, you won't want to have to nest a new view each time you go down the breadcrumb trail. To avoid using nested views, you should use a named view and refer to it when configuring your states. See the demo and the example below for an idea of how this would work.

Installation

1. Download

Download the file uiBreadcrumbs.js.

2. Include in your app

Make sure the file uiBreadcrumbs.js is being loaded in your app.

Declare the dependency in your Angular module:

angular.module('myApp', ['ui.breadcrumbs']);

Usage

Assuming you already have your app configured to use ui-router, you then need to put this directive somewhere and use it like so:

<ui-breadcrumbs displayname-property="data.displayName"
                [abstract-proxy-property=""]>
</ui-breadcrumbs>
  • displayname-property (required) This attribute must point to some property on your state config object that contains the string you wish to display as the route's breadcrumb. If none is specified, or if the specified property is not found, the directive will default to displaying the route's name.
  • abstract-proxy-property (optional) Used when working with abstract states. See the section on working with abstract states below for a full explanation.

Example setup

He is an example that illustrates the main features of the directive:

angular.module('yourModule').config(function($stateProvider) {
    $stateProvider
        .state('home', {
            url: '/',
            views: {
                'content@': {
                    templateUrl: ...
                }
            },
            data: {
                displayName: 'Home'
            }
        })
        .state('home.usersList', {
            url: 'users/',
            views: {
                'content@': {
                    templateUrl: ...
                }
            },
            data: {
                displayName: 'Users'
            }
        })
        .state('home.userList.detail', {
            url: ':id',
            views: {
                'content@': {
                    templateUrl: ...
                }
            },
            data: {
                displayName: '{{ user.firstName }} {{ user.lastName | uppercase }}'
            }
            resolve: {
                user : function($stateParams, userService) {
                    return userService.getUser($stateParams.id);
                }
            }
        })
        .state('home.userList.detail.image', {
            views: {
                'content@': {
                    templateUrl: ...
                }
            },
            data: {
                displayName: false
            }
        })
<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>
<div ui-view="content"></div>

The first two states are straightforward. The property specified in the displayname-property attribute can be seen to exist in the config object, the value of which is a string with the name we want to display in the breadcrumb.

The third state illustrates how we can use a resolved value as our breadcrumb display name. This is done by using the regular Angular interpolation syntax {{ value }}. In this case, user corresponds to the resolve function that is using our imaginary userService to asynchronously grab an object containing the details of the current user. You can also see that, just like in any Angular interpolation string, you can reference properties of objects, use filters and so on.

The fourth state illustrates that if we don't want a state to show up in the breadcrumbs, we should set the display name to false.

Working With Abstract States

AngularUI Router provides the option of setting up abstract states, which by definition cannot be transitioned to. Therefore, we cannot show them in the breadcrumbs, as clicking on an abstract state would cause the router to try to transition to that state, which results in an exception.

Therefore, the default behaviour is to ignore abstract states and skip that level of the breadcrumb hierarchy.

However, in many cases this behaviour would not be desirable. Consider the following setup (taken from the ui-router docs):

$stateProvider
    .state('contacts', {
        abstract: true,
        url: '/contacts',

        // Note: abstract still needs a ui-view for its children to populate.
        // You can simply add it inline here.
        template: '<ui-view/>'
    })
    .state('contacts.list', {
        // url will become '/contacts/list'
        url: '/list'
        //...more
    })
    .state('contacts.detail', {
        // url will become '/contacts/detail'
        url: '/detail',
        //...more
    })

In this case, if we were in the contacts.detail state, the breadcrumbs would only display that state and ignore the parent state as it is abstract. What we really want to do is substitute the contacts.list state for the parent state. This is because, logically, the list of contacts is one level up from the detail page, even though they are strictly at the same level in the $state definition.

In order to achieve this substitution, we can use the abstract-proxy-property attribute on our directive. This tells the directive to look for the specified property on the state config object, where it should find the name of the state to use instead of the abstract state.

To implement this, we would modify the above example to look like this:

$stateProvider
    .state('contacts', {
        abstract: true,
        url: '/contacts',
        template: '<ui-view/>'
        data: {
            breadcrumbProxy: 'contacts.list'
        }
    })
    .state('contacts.list', {
        url: '/list'
        //...more
    })
    .state('contacts.detail', {
        url: '/detail',
        //...more
    })

The directive element would then look like this:

<ui-breadcrumbs displayname-property="data.displayName" abstract-proxy-property="data.breadcrumbProxy"></ui-breadcrumbs>

Now, when we are in the contacts.detail state, the breadcrumbs will show the contacts.list as the immediate parent, rather than the abstract contacts state.

Triggering Update

It is possible to force breadcrumbs to update without a state change by broadcasting a updateBreadcrumbsArray event from the $rootscope.

$rootScope.$broadcast('updateBreadcrumbsArray');

Styling

The template structure is based on the Bootstrap 3 breadcrumbs component, so it includes an active class to signify the current (left-most) item in the breadcrumbs list. You can, of course, modify the template as needed or simply define your own CSS to fit it with your app's style.

Note on the data object

A potential "gotcha" is the fact that the data object gets inherited by child states (see docs here). If you want to avoid this behaviour, don't use the data object to define your breadcrumb's displayname-property. Use another property on the .state() config object instead.

Credits

I used some ideas and approaches from the following sources:

License

MIT license.

The original code was provided under the MIT license.