Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 45x 3x 3x 3x 3x 3x 3x 2x 3x 3x 3x 3x 3x 3x 3x 10x 10x 10x 10x 3x 3x 7x 7x 7x 10x 10x 10x 22x 22x 11x 22x 10x | /** * Copyright (c) Siemens 2016 - 2025 * SPDX-License-Identifier: MIT */ import { Component, inject, input, OnDestroy, OnInit, signal } from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; import { BreadcrumbItem, SiBreadcrumbComponent } from '@siemens/element-ng/breadcrumb'; import { Observable, Subject, Subscription } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { SI_BREADCRUMB_RESOLVER_SERVICE } from './si-breadcrumb-router.model'; @Component({ selector: 'si-breadcrumb-router', imports: [SiBreadcrumbComponent], templateUrl: './si-breadcrumb-router.component.html' }) export class SiBreadcrumbRouterComponent implements OnInit, OnDestroy { /** * Aria label for the main breadcrumb navigation. Needed for a11y. * * @defaultValue 'breadcrumb' */ readonly ariaLabel = input('breadcrumb'); protected readonly items = signal<BreadcrumbItem[]>([]); private readonly currentCalcUrl = signal<string | undefined>(undefined); private nextRoute = new Subject<void>(); private resolverService = inject(SI_BREADCRUMB_RESOLVER_SERVICE); private route? = inject(ActivatedRoute, { optional: true }); private router? = inject(Router, { optional: true }); private routerSubscription?: Subscription; ngOnInit(): void { this.checkItems(); } ngOnDestroy(): void { this.routerSubscription?.unsubscribe(); this.nextRoute.next(); } private checkItems(): void { if (!this.routerSubscription && this.route && this.router) { this.routerSubscription = this.router.events .pipe(filter(e => e instanceof NavigationEnd)) .subscribe(navigationEvent => { const event = navigationEvent as NavigationEnd; // Get the new url const newUrl = event.urlAfterRedirects || event.url; // Only update when url differs from previous url if (this.currentCalcUrl() !== newUrl) { this.currentCalcUrl.set(newUrl); this.nextRoute.next(); this.computePath(); } }); Iif (this.router.navigated) { this.currentCalcUrl.set(this.router.url); this.computePath(); } } } private computePath(): void { Iif (!this.route || !this.resolverService) { return; } // Get a snapshot of the all current activate routes const pathFromRoot: ActivatedRouteSnapshot[] = this.route.snapshot.pathFromRoot; // Find the child/leaf route that fits to the url const route = this.findRouteWithUrl(pathFromRoot, this.currentCalcUrl()?.split('?')[0] ?? ''); if (route) { // Workaround to fix a bug that the route is null, in some cases const links$ = this.resolverService.resolve(route); Iif (links$ instanceof Observable) { links$.pipe(takeUntil(this.nextRoute)).subscribe(links => { this.items.set([{ link: '/', title: '/' }, ...links]); }); } else { this.items.set([{ link: '/', title: '/' }, ...links$]); } } } private findRouteWithUrl( routes: ActivatedRouteSnapshot[], url: string ): ActivatedRouteSnapshot | null { let result: ActivatedRouteSnapshot | null = null; for (const route of routes) { const routeUrl = this.getUrl(route); if (url === routeUrl && !route.data?.siBreadcrumbIgnore) { result = route; break; } else { result = this.findRouteWithUrl(route.children, url); if (result != null) { break; } } } return result; } private getUrl(route: ActivatedRouteSnapshot): string { let url = ''; for (const routeSegment of route.pathFromRoot) { const myUrl: string = routeSegment.url.map(o => o.toString()).join('/'); if (!url.endsWith('/')) { url = url + '/'; } url = url + myUrl; } return url; } } |