File

src/app/core/navigation/breadcrumbs/breadcrumbs.component.ts

Implements

OnDestroy

Metadata

selector cd-breadcrumbs
styleUrls ./breadcrumbs.component.scss
templateUrl ./breadcrumbs.component.html

Index

Properties
Methods

Constructor

constructor(router: Router, injector: Injector)
Parameters :
Name Type Optional
router Router No
injector Injector No

Methods

Private _resolveCrumbs
_resolveCrumbs(route: ActivatedRouteSnapshot)
Parameters :
Name Type Optional
route ActivatedRouteSnapshot No
isPromise
isPromise(value: any)
Parameters :
Name Type Optional
value any No
Returns : boolean
ngOnDestroy
ngOnDestroy()
Returns : void
postProcess
postProcess(breadcrumbs: IBreadcrumb[])
Parameters :
Name Type Optional
breadcrumbs IBreadcrumb[] No
Returns : {}
wrapIntoObservable
wrapIntoObservable(value: T | Promise | Observable)
Type parameters :
  • T
Parameters :
Name Type Optional
value T | Promise<T> | Observable<T> No
Returns : Observable<T>

Properties

crumbs
Type : IBreadcrumb[]
Default value : []
Private defaultResolver
Default value : new BreadcrumbsResolver()
finished
Default value : false

Usefull for e2e tests. This allow us to mark the breadcrumb as pending during the navigation from one page to another. This resolves the problem of validating the breadcrumb of a new page and still get the value from the previous

subscription
Type : Subscription
import { Component, Injector, OnDestroy } from '@angular/core';
import { ActivatedRouteSnapshot, NavigationEnd, NavigationStart, Router } from '@angular/router';

import { from, Observable, of, Subscription } from 'rxjs';
import { concat, distinct, filter, first, flatMap, toArray } from 'rxjs/operators';

import { BreadcrumbsResolver, IBreadcrumb } from '../../../shared/models/breadcrumbs';

@Component({
  selector: 'cd-breadcrumbs',
  templateUrl: './breadcrumbs.component.html',
  styleUrls: ['./breadcrumbs.component.scss']
})
export class BreadcrumbsComponent implements OnDestroy {
  crumbs: IBreadcrumb[] = [];
  /**
   * Usefull for e2e tests.
   * This allow us to mark the breadcrumb as pending during the navigation from
   * one page to another.
   * This resolves the problem of validating the breadcrumb of a new page and
   * still get the value from the previous
   */
  finished = false;
  subscription: Subscription;
  private defaultResolver = new BreadcrumbsResolver();

  constructor(private router: Router, private injector: Injector) {
    this.subscription = this.router.events
      .pipe(filter((x) => x instanceof NavigationStart))
      .subscribe(() => {
        this.finished = false;
      });

    this.subscription = this.router.events
      .pipe(filter((x) => x instanceof NavigationEnd))
      .subscribe(() => {
        const currentRoot = router.routerState.snapshot.root;

        this._resolveCrumbs(currentRoot)
          .pipe(
            flatMap((x) => x),
            distinct((x) => x.text),
            toArray(),
            flatMap((x) => {
              const y = this.postProcess(x);
              return this.wrapIntoObservable<IBreadcrumb[]>(y).pipe(first());
            })
          )
          .subscribe((x) => {
            this.finished = true;
            this.crumbs = x;
          });
      });
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }

  private _resolveCrumbs(route: ActivatedRouteSnapshot): Observable<IBreadcrumb[]> {
    let crumbs$: Observable<IBreadcrumb[]>;

    const data = route.routeConfig && route.routeConfig.data;

    if (data && data.breadcrumbs) {
      let resolver: BreadcrumbsResolver;

      if (data.breadcrumbs.prototype instanceof BreadcrumbsResolver) {
        resolver = this.injector.get(data.breadcrumbs);
      } else {
        resolver = this.defaultResolver;
      }

      const result = resolver.resolve(route);
      crumbs$ = this.wrapIntoObservable<IBreadcrumb[]>(result).pipe(first());
    } else {
      crumbs$ = of([]);
    }

    if (route.firstChild) {
      crumbs$ = crumbs$.pipe(concat(this._resolveCrumbs(route.firstChild)));
    }

    return crumbs$;
  }

  postProcess(breadcrumbs: IBreadcrumb[]) {
    const result = [];
    breadcrumbs.forEach((element) => {
      const split = element.text.split('/');
      if (split.length > 1) {
        element.text = split[split.length - 1];
        for (let i = 0; i < split.length - 1; i++) {
          result.push({ text: split[i], path: null });
        }
      }
      result.push(element);
    });
    return result;
  }

  isPromise(value: any): boolean {
    return value && typeof value.then === 'function';
  }

  wrapIntoObservable<T>(value: T | Promise<T> | Observable<T>): Observable<T> {
    if (value instanceof Observable) {
      return value;
    }

    if (this.isPromise(value)) {
      return from(Promise.resolve(value));
    }

    return of(value as T);
  }
}
<ol *ngIf="crumbs.length"
    class="breadcrumb">
  <li *ngFor="let crumb of crumbs; let last = last"
      [ngClass]="{ 'active': last && finished }"
      class="breadcrumb-item">
    <a *ngIf="!last && crumb.path !== null"
       [routerLink]="crumb.path">{{ crumb.text }}</a>
    <span *ngIf="last || crumb.path === null">{{ crumb.text }}</span>
  </li>
</ol>

./breadcrumbs.component.scss

@import '../../../../defaults';

.breadcrumb {
  padding: 8px 0;
  background-color: transparent;
  border-radius: 0;
}

.breadcrumb > li + li:before {
  padding: 0 5px 0 7px;
  color: $color-breadcrumb;
  font-family: 'ForkAwesome';
  content: '\f101';
}

.breadcrumb > li > span {
  color: $color-breadcrumb;
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""