Breadcrumbs are a crucial navigational aid in web applications, providing users with a clear path of where they are within the application's hierarchy. In Angular applications, implementing breadcrumbs can be straightforward yet effective. In this article, we'll discuss how to create simple breadcrumbs to display the link path in an Angular app, along with some code examples.
Understanding Breadcrumbs
Before diving into the implementation, let's briefly understand what breadcrumbs are and why they are important.
A "breadcrumb" or "breadcrumb trail" is a secondary navigation feature that showcases the user's current location within a website or web application. Originating from the Hansel and Gretel fairy tale, where the titular children leave breadcrumbs to mark their path home, this navigation element serves as a virtual trail for users to retrace their steps back to their starting point. Much like the tale, breadcrumbs in digital applications provide users with a clear route back to their initial landing spot.
Breadcrumbs typically appear at the top of a web page and show the hierarchical path from the homepage to the current page. They help users understand their current location within the website structure and provide a way to navigate back to previous pages. Breadcrumbs can indicate a location, a specific category, or a particular page, and they may also present the full path of the page. However, for the purpose of this example, we will focus on following the path to the current page and displaying all its segments as a new URL.
Implementation in Angular
In Angular applications, we can create breadcrumbs by utilizing Angular's routing mechanism and a service to manage the breadcrumb data. Let's go through a simple implementation step by step.
Step 1: Create a Breadcrumbs Service
In this step, we lay the foundation for our breadcrumb functionality by creating a service dedicated to managing breadcrumb data. This service plays a crucial role in our Angular application, as it listens to route changes and dynamically updates the breadcrumb trail accordingly.
What does the Breadcrumbs Service do? The Breadcrumbs Service acts as a centralized repository for storing and updating breadcrumb data. This is how our breadcrumbs.service.ts file looks like:
// breadcrumbs.service.ts
import { Injectable } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class BreadcrumbsService {
private breadcrumbs: { label: string; url: string }[] = [];
constructor(private router: Router) {
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.subscribe(() => {
this.generateBreadcrumbs();
});
}
private generateBreadcrumbs(): void {
this.breadcrumbs = [];
const urlSegments: string[] = this.router.url.split('/').filter((segment) => segment !== '');
let url = '';
for (const segment of urlSegments) {
url += `/${segment}`;
this.breadcrumbs.push({ label: segment, url: url });
}
}
getBreadcrumbs(): { label: string; url: string }[] {
return this.breadcrumbs;
}
}
Let's break down its functionality:
- Initialization: Upon instantiation, the service initializes an empty array to store breadcrumb items:
private breadcrumbs: { label: string; url: string }[] = [];
- Router Events Subscription: The Router module is injected into the constructor and the service subscribes to router events emitted by Angular's Router module. Specifically, it listens for NavigationEnd events. These events are triggered when a navigation action has successfully completed, indicating a change in the route hierarchy:
constructor(private router: Router) { this.router.events .pipe(filter((event) => event instanceof NavigationEnd)) .subscribe(() => { this.generateBreadcrumbs(); }); }
- Event Filtering: Using RxJS operators, particularly the filter operator, the service ensures that it only responds to NavigationEnd events. This filtering mechanism ensures that breadcrumb generation occurs only when necessary, optimizing performance.
- Breadcrumb Generation: When a NavigationEnd event is detected, the service triggers a private method called generateBreadcrumbs(). This method is responsible for dynamically generating breadcrumb items based on the current route.
- Updating Breadcrumbs: Within the generateBreadcrumbs() method, the service extracts the URL segments from the current route using the Router module's url property. It then iterates through these segments, constructing the breadcrumb trail incrementally. Each breadcrumb item consists of a segment label (representing the name of the route segment) and a URL (representing the path up to that segment).
- Accessing Breadcrumbs: Finally, the service provides a public method named getBreadcrumbs(), allowing other components within the application to retrieve the breadcrumb data stored in the service.
Step 2: Generate Breadcrumbs
In the generateBreadcrumbs() method, we extract the URL segments from the current route and generate breadcrumb items.
private generateBreadcrumbs(): void {
this.breadcrumbs = [];
const urlSegments: string[] = this.router.url.split('/').filter((segment) => segment !== '');
let url = '';
for (const segment of urlSegments) {
url += `/${segment}`;
this.breadcrumbs.push({ label: segment, url: url });
}
}
Step 3: Display Breadcrumbs in Component
Now, we create a component to display the breadcrumbs using the Breadcrumbs service.
// breadcrumbs.component.ts
import { Component } from '@angular/core';
import { BreadcrumbsService } from '../shared/breadcrumbs.service';
@Component({
selector: 'app-breadcrumbs',
templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.css']
})
export class BreadcrumbsComponent {
constructor(public breadcrumbsService: BreadcrumbsService) {}
}
To simplify the process, we directly inject the service into the constructor and we used it as it is in the template.
Step 4: Template for Breadcrumbs
Finally, we create a template to render the breadcrumbs in the UI.
<!-- breadcrumbs.component.html -->
<header class="page-header">
<h2>{{ breadcrumbsService.getBreadcrumbs()[breadcrumbsService.getBreadcrumbs().length - 1].label | titlecase}}</h2>
<div class="right-wrapper text-end">
<ol class="breadcrumbs">
<li *ngFor="let breadcrumb of breadcrumbsService.getBreadcrumbs();">
<a [routerLink]="breadcrumb.url">{{ breadcrumb.label | titlecase }}</a>
</li>
</ol>
</div>
</header>
We are using "ngFor" structural directive to iterate throughout the url segments. We are calling getBreadcrumbs() method in the injected breadcrumbsService service directly in the ngFor loop.
There are two parts of each breadcrumbs item, the Label and the URL. In the template we are displaying the Label in the link to the page and also as a title of the current page.
<h2>{{ breadcrumbsService.getBreadcrumbs()[breadcrumbsService.getBreadcrumbs().length - 1].label | titlecase}}</h2>
In the above code we are taking the label from the last element in the breadcrumbs array, we apply the titlecase pipe to make the first letter of the label uppercase, and we display it with h2 tags, as a page title for the current page.
In the ngFor
loop, each segment is displayed as url and label and we also apply titlecase pipe to breadcrumb.label.
<a [routerLink]="breadcrumb.url">{{ breadcrumb.label | titlecase }}</a>
This is how simple it is to create simple breadcrumbs in Angular applications!
In any application, various approaches and solutions exist for each problem. In the breadcrumbs service we've created, we opted not to utilize child routes from the app-router.module.ts file. Instead, we extract segments from the path to serve as the basis for our breadcrumbs. We chose this method because exclusively relying on child routes isn't always the most suitable approach for structuring an Angular application. There are scenarios where you may have deeply nested routes leading to individual components customized for specific functionalities. In such cases, concealing parent components in the breadcrumb trail is preferable, making this approach more suitable than relying solely on child routes.
By using a breadcrumbs service to manage the breadcrumb data and a component to render the breadcrumbs in the UI, we can provide users with a clear navigation path within the application. While this implementation does not account for route children, it provides a solid foundation for breadcrumb navigation in most Angular applications.