import { Injectable } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router, } from '@angular/router'; import { combineLatest, Observable, of as observableOf, ReplaySubject, } from 'rxjs'; import { filter, map, switchMap, tap, } from 'rxjs/operators'; import { hasNoValue, hasValue, isUndefined, } from '../shared/empty.util'; import { Breadcrumb } from './breadcrumb/breadcrumb.model'; @Injectable({ providedIn: 'root', }) export class BreadcrumbsService { /** * Observable of the list of breadcrumbs for this page */ breadcrumbs$: ReplaySubject<Breadcrumb[]> = new ReplaySubject(1); /** * Whether or not to show breadcrumbs on this page */ showBreadcrumbs$: ReplaySubject<boolean> = new ReplaySubject(1); constructor( private route: ActivatedRoute, private router: Router, ) {} /** * Called by {@link AppComponent#constructor} (i.e. before routing) * such that no routing events are missed. */ listenForRouteChanges() { // supply events to this.breadcrumbs$ this.router.events.pipe( filter((e): e is NavigationEnd => e instanceof NavigationEnd), tap(() => this.reset()), switchMap(() => this.resolveBreadcrumbs(this.route.root)), ).subscribe(this.breadcrumbs$); } /** * Method that recursively resolves breadcrumbs * @param route The route to get the breadcrumb from */ private resolveBreadcrumbs(route: ActivatedRoute): Observable<Breadcrumb[]> { const data = route.snapshot.data; const routeConfig = route.snapshot.routeConfig; const last: boolean = hasNoValue(route.firstChild); if (last) { if (hasValue(data.showBreadcrumbs)) { this.showBreadcrumbs$.next(data.showBreadcrumbs); } else if (isUndefined(data.breadcrumb)) { this.showBreadcrumbs$.next(false); } } if ( hasValue(data) && hasValue(data.breadcrumb) && hasValue(routeConfig) && hasValue(routeConfig.resolve) && hasValue(routeConfig.resolve.breadcrumb) ) { const { provider, key, url } = data.breadcrumb; if (!last) { return combineLatest(provider.getBreadcrumbs(key, url), this.resolveBreadcrumbs(route.firstChild)) .pipe(map((crumbs) => [].concat.apply([], crumbs))); } else { return provider.getBreadcrumbs(key, url); } } return !last ? this.resolveBreadcrumbs(route.firstChild) : observableOf([]); } /** * Resets the state of the breadcrumbs */ private reset() { this.showBreadcrumbs$.next(true); } }