@exalif/ngx-breadcrumbs

Angular 4+ breadcrumbs on top of native Angular router

Stats

StarsIssuesVersionUpdatedCreatedSize
@exalif/ngx-breadcrumbs
Minified + gzip package size for @exalif/ngx-breadcrumbs in KB

Readme

@exalif/ngx-breadcrumbs

An Angular (4+) module generating breadcrumbs based on the routing state.

This package is a modified fork of original ngx-breadcrumbs package by McNull

Requirements

Caution: Version >=9.0.0 requires:

  • rxjs >=6.3.0
  • @angular >=9.0.0

note: lodash is not required anymore on this version

Caution: Version >=8.0.0 requires:

  • rxjs >=6.3.0
  • @angular >=9.0.0
  • you must install lodash.template as a dependency in your project as it was moved to peer dependency on this version

Caution: Version >=7.0.0 requires:

  • rxjs >=6.3.0
  • @angular >=7.0.0

Caution: Version >=6.0.0 requires:

  • rxjs >=6.0.0
  • @angular >=6.0.0

Installation

# install via npm
$ npm --save install @exalif/ngx-breadcrumbs
# install via yarn
$ yarn add @exalif/ngx-breadcrumbs

Usage

Import the BreadcrumbsModule in your root module (app.module.ts) after importing the router module.

You can provide an initial BreadcrumbsConfig as forRoot argument. By default, if no config is provided, the applied config will correspond to the one provided in the following example:

import { RouterModule } from '@angular/router';
import { BreadcrumbsModule } from '@exalif/ngx-breadcrumbs';

@NgModule({
  imports: [
    RouterModule.forRoot(myRoutes),
    BreadcrumbsModule.forRoot({
      postProcess: null,
      applyDistinctOn: 'text',
    }),
  ],  
})
export class AppModule {}

Place the lib-breadcrumbs component, which will render the breadcrumbs, somewhere in your markup.

@Component({
  selector: 'app-root',
  template: `
    <div class="container">
      <lib-breadcrumbs></lib-breadcrumbs>
      <router-outlet></router-outlet>
    </div>`
})
export class AppComponent {}

Usage of the lib-breadcrumbs render component is optional. If a different markup output is desired, a custom component can be created that subscribes to the BreadcrumbsService.crumbs$ observable (also available through method BreadcrumbsService.getCrumbs()).

Routing Configuration

Breadcrumbs links are generated based on the route configuration. If a route entry contains a data.breadcrumbs property the breadcrumbs service assumes breadcrumbs should be created whenever this route or one its child routes are active.

const myRoutes : Route[] = {
  {
    path: '',
    component: HomeComponent,
    data: {
      // Uses static text (Home)
      breadcrumbs: 'Home' 
    }
  },
  {
    path: 'about',
    component: AboutComponent,
    data: {
      // Uses last urlfragment (about) as text
      breadcrumbs: true 
    }
  },
  {
    path: 'person',
    data: {
      // Uses text property (Person)
      breadcrumbs: true,
      text: 'Person'
    },
    children: [
      {
          path: '',
          component: PersonListComponent
      },
      {
          path: ':id',
          component: PersonDetailComponent,
          data: {
            // Interpolates values resolved by the router 
            breadcrumbs: '{{ person.name }}'
          },
          resolve: {
            person: PersonResolver
          }
      } 
    ]
  },    
  {
    path: 'folder',
    data: {
      // Uses static text 'Folder'
      breadcrumbs: 'Folder'
    },
    children: [
      {
      path: '',
      component: FolderComponent
      },
      {
        path: ':id',
        component: FolderComponent,
        data: {
          // Resolves the breadcrumbs for this route by
          // implementing a BreadcrumbsResolver class.
          breadcrumbs: FolderBreadcrumbsResolver
        }
      }
    ]
  }
};

API

Breadcrumb

The Breadcrumb interface defines the properties of the breadcrumb items.

export interface Breadcrumb {
  text: string,  // The text to display 
  path: string   // The associated path
}

BreadcrumbsComponent

The component simply renders the list of the Breadcrumb items provided by the BreadcrumbsService.

A custom breadcrumb component is easily created by injecting the breadcrumb service and iterating over the breadcrumb items.

Styling

The following CSS classes can be used for styling purpose:

  • breadcrumbs__container: ol list container
  • breadcrumbs__item: each breadcrum li element
  • breadcrumbs__item--active: added on last li element (current active link)

Each breadcrumb li element contains a a link and a span text element.

BreadcrumbsService

The service has one public property crumbs$. It's an observable stream of Breadcrumb[], which is updated after each route change.

BreadcrumbsResolver

If needed, a custom resolver can be implemented which is activated whenever a certain route becomes active. This can be useful whenever the route configuration cannot match the desired breadcrumb hierachy.

The signature of the resolver implements Resolve<T> from the Angular Router and needs to resolve an array of Breadcrumb items.

To associate a route with a certain resolver, its breadcrumbs data property in the route configuration should point to the resolver:

const myRoutes = [
  {
    path: 'somepath',
    component: SomeComponent,
    data: {
      breadcrumbs: MyBreadcrumbsResolver
    }
  }
];
Members
// Should resolve zero or more Breadcrumb items.
public function resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
  : Observable<T> | Promise<T> | T 
// Helper function that returns the full path for the provided route snapshot.
public function getFullPath(route: ActivatedRouteSnapshot) 
  : string

To access parent member without redeclaring method, use super. e.g: super.getFullPath(route).

Example using a custom resolver and custom Breadcrumb interface

export interface MyBreadcrumb extends Breadcrumb {
  icon?: string;
}

@Injectable()
export class MyBreadcrumbsResolver extends BreadcrumbsResolver {

  // Optional: inject any required dependencies
  constructor(private myService: MyService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MyBreadcrumb[] {    
    const myFolderId = route.params.id;
    const icon: string = route.data.icon ? route.data.icon : null;

    const myCrumbs = 
      this.myService.getFolders(myFolderId).pipe(
        .map((folder) => ({
          text: folder.title,
          icon
          path: super.getFullPath(route.parent) + '/' + folder.id
        }));

    return myCrumbs; 
  }
}

Note: the resolve method must return one of the following types:

  • Breadcrumb[]
  • Observable<Breadcrumb[]>
  • Promise<Breadcrumb>

BreadcrumbsConfig

The configuration of the breadcrumbs module.

Members

postProcess

Callback function with the following signature:

function (crumbs: Breadcrumb[]): Promise<Breadcrumb[]> | Observable<Breadcrumb[]> | Breadcrumb[];

Can be used to make custom changes to the breadcrumb array after the service has constructed the breadcrumb trail.

Example:

@NgModule({
  /* ... */
})
export class AppModule {
  constructor(breadcrumbsConfig: BreadcrumbsConfig) {

    breadcrumbsConfig.postProcess = (breadcrumbs): Breadcrumb[] => {

      // Ensure that the first breadcrumb always points to home
      let processedBreadcrumbs = breadcrumbs;

      if(breadcrumbs.length && breadcrumbs[0].text !== 'Home') {
        processedBreadcrumbs = [
          {
            text: 'Home',
            path: ''
          }
        ].concat(breadcrumbs);
      }

      return processedBreadcrumbs;
    };
  }
}

applyDistinctOn

By default, distinct rxjs operator is applied on text key of Breadcrumb, not to add crumbs with same text in the breadcrumbs array. If this is not correct behavior in your app, you can change applyDistinctOn key to another Breadcrumb key or to null to disable this behavior.

Signature:

applyDistinctOn: DistinctKey | null = 'text';

Usage example to remove distinct behavior:

import { RouterModule } from '@angular/router';
import { BreadcrumbsModule } from '@exalif/ngx-breadcrumbs';

@NgModule({
  imports: [
    RouterModule.forRoot(myRoutes),
    BreadcrumbsModule.forRoot({
      applyDistinctOn: null,
    }),
  ],  
})
export class AppModule {}

If you find any bugs or have a feature request, please open an issue on github!

The npm package download data comes from npm's download counts api and package details come from npms.io.