- Type
<vcs-breadcrumbs></vcs-breadcrumbs>
<vcs-breadcrumbs [type]="'long'"></vcs-breadcrumbs>
<vcs-breadcrumbs [type]="'long-wrap'"></vcs-breadcrumbs>
<vcs-breadcrumbs [type]="'truncation'"></vcs-breadcrumbs>
- Modules
import { BreadcrumbsModule } from 'vcs-material';
const routes: Routes = [
// breadcrumbs with static lable
{
path: 'breadcrumbs',
component: BreadcrumbsComponent,
data: {
breadcrumb: [
{
label: 'Home',
url: '/',
},
{
label: 'Breadcrumbs',
url: '/component/breadcrumbs',
icon: 'vcs-layout-dashboard'
},
{
label: 'Detail',
}
]
}
},
// breadcrumbs with dynamic lable ( get data from params )
{
path: ':id/:name/detail',
component: DatasourceDetailComponent,
data: {
breadcrumb: [
{
label: 'Home',
url: '/'
},
{
label: 'Datasources',
url: '/datasource-management'
},
{
label: '//:name//',
}
]
}
}
]
@NgModule({
imports: [
BreadcrumbsModule,
RouterModule.forChild(routes)
]
})
<ng-container *ngIf="breadcrumbConfigs && breadcrumbConfigs.length">
<ul class="breadcrumb" *ngIf="(type !== 'truncation') || ( type == 'truncation' && breadcrumbConfigs.length <= 2)" [ngClass]="{
'long-wrap': type == 'long-wrap',
'long': type == 'long'
}">
<li *ngFor="let breadcrumb of breadcrumbConfigs; let i = index;">
<ng-container>
<mat-icon *ngIf="breadcrumb.icon" svgIcon="{{ breadcrumb.icon }}" color="primary" class="small breadcrumb-icon"></mat-icon>
<a [routerLink]="breadcrumb.url" *ngIf="breadcrumb.url" routerLinkActive="router-link-active">
{{ breadcrumb.label }}
</a>
<span *ngIf="!breadcrumb.url" routerLinkActive="router-link-active">
{{ breadcrumb.label }}
</span>
<span class="separate-character">
{{separateCharacter}}
</span>
</ng-container>
</li>
</ul>
<ul class="breadcrumb truncation" *ngIf="type === 'truncation' && breadcrumbConfigs.length > 2 ">
<li>
<mat-icon *ngIf="breadcrumbConfigs[0].icon" svgIcon="{{ breadcrumbConfigs[0].icon }}" color="primary" class="small breadcrumb-icon"></mat-icon>
<a [routerLink]="breadcrumbConfigs[0].url" *ngIf="breadcrumbConfigs[0].url" routerLinkActive="router-link-active">
{{ breadcrumbConfigs[0].label }}
</a>
<span *ngIf="!breadcrumbConfigs[0].url" routerLinkActive="router-link-active">
{{ breadcrumbConfigs[0].label }}
</span>
<span class="separate-character">
{{separateCharacter}}
</span>
</li>
<li>
<span>...</span>
<span class="separate-character">
{{separateCharacter}}
</span>
</li>
<li>
<mat-icon *ngIf="breadcrumbConfigs[breadcrumbConfigs.length - 1].icon" svgIcon="{{ breadcrumbConfigs[breadcrumbConfigs.length - 1].icon }}" color="primary" class="small breadcrumb-icon"></mat-icon>
<a [routerLink]="breadcrumbConfigs[breadcrumbConfigs.length - 1].url" *ngIf="breadcrumbConfigs[breadcrumbConfigs.length - 1].url" routerLinkActive="router-link-active">
{{ breadcrumbConfigs[breadcrumbConfigs.length - 1].label }}
</a>
<span *ngIf="!breadcrumbConfigs[breadcrumbConfigs.length - 1].url" routerLinkActive="router-link-active">
{{ breadcrumbConfigs[breadcrumbConfigs.length - 1].label }}
</span>
<span class="separate-character">
{{separateCharacter}}
</span>
</li>
</ul>
</ng-container>
$breadcrumb-color: #848792;
$breadcrumb-active-color: #1a77c7 !important;
$font-family: 'FVHCircularXX-Medium';
$font-size: 14px;
$height-item: 28px; // = 2 * $font-size
.d-none {
display: none !important;
}
@mixin baseTextStyle {
font-size: $font-size;
font-family: $font-family;
color: $breadcrumb-color;
white-space: nowrap;
}
.breadcrumb {
background: none;
width: fit-content;
font-size: $font-size;
display: block;
margin: 0;
display: flex;
padding-left: 0;
width: 100%;
flex-wrap: wrap;
&.long {
overflow: auto;
flex-wrap: nowrap;
}
&.long-wrap {
overflow: auto;
flex-wrap: wrap;
}
&.truncation {
overflow: auto;
flex-wrap: nowrap;
}
a {
@include baseTextStyle;
&:hover {
color: $breadcrumb-active-color !important;
text-decoration: none;
}
}
span {
@include baseTextStyle;
}
li {
list-style: none;
float: left;
margin: 0 4px;
display: flex;
justify-content: center;
align-items: center;
line-height: $height-item;
height: $height-item;
.breadcrumb-icon {
font-size: 16px;
margin-right: 4px;
transform: scale(0.8);
}
.separate-character {
color: $breadcrumb-color;
font-size: 16px;
margin-left: 8px;
}
.toggle-collapse {
cursor: pointer;
}
&:last-child {
&::after {
content: "";
}
.separate-character {
display: none;
}
}
}
}
// https://dev.to/zhiyueyi/create-a-simple-breadcrumb-in-angular-ag5
import { ChangeDetectorRef, Input } from '@angular/core';
import { Component, OnInit } from "@angular/core";
import {
ActivatedRoute,
Router,
NavigationEnd,
} from "@angular/router";
import { filter } from "rxjs/operators";
@Component({
selector: 'vcs-breadcrumbs',
templateUrl: './breadcrumbs.component.html',
styleUrls: ['./breadcrumbs.component.scss']
})
export class BreadcrumbsComponent implements OnInit {
@Input() type = 'long-wrap'; // 'long', 'long-wrap', 'truncation';
@Input() separateCharacter = '/'; // '/', ',', '.';
collapsed = true;
breadcrumbConfigs: Breadcrumb[] = [];
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private cd: ChangeDetectorRef
) {
}
ngOnInit() {
this.breadcrumbConfigs = this.buildBreadCrumb(this.activatedRoute.root);
this.cd.detectChanges();
this.router.events
.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
this.breadcrumbConfigs = this.buildBreadCrumb(this.activatedRoute.root);
window.scrollTo(0, 0);
this.cd.detectChanges();
});
}
buildBreadCrumb(route: ActivatedRoute, url: string = '', breadcrumbs: Breadcrumb[] = []): Breadcrumb[] {
let sliceChar = '//';
//If no routeConfig is avalailable we are on the root path
let breadcrumbConfigs = route.routeConfig && route.routeConfig.data ? route.routeConfig.data.breadcrumb : '';
let path = route.routeConfig && route.routeConfig.data ? route.routeConfig.path : '';
// If the route is dynamic route such as ':id', remove it and exchange with data from params
if (breadcrumbConfigs && breadcrumbConfigs.length) {
breadcrumbConfigs = breadcrumbConfigs.map((breadCrumb: Breadcrumb) => {
const breadCrumbLabelSplits = breadCrumb.label.split(sliceChar);
// console.log(breadCrumbLabelSplits)
const breadCrumbWithLabelMappingVal: string[] = breadCrumbLabelSplits.map(label => {
if (label.charAt(0) === ':') {
const labelMappingParam = route.snapshot.params[label.slice(1, label.length)];
label = labelMappingParam ? labelMappingParam : label;
}
return label;
});
return {
label: breadCrumbWithLabelMappingVal.join(''),
url: breadCrumb.url,
icon: breadCrumb.icon,
};
});
}
//In the routeConfig the complete path is not available,
//so we rebuild it each time
const nextUrl = path ? `${url}/${path}` : url;
// Only adding route with non-empty label
const newBreadcrumbs = breadcrumbConfigs ? breadcrumbs.concat(breadcrumbConfigs) : breadcrumbs;
if (route.firstChild) {
//If we are not on our current path yet,
//there will be more children to look after, to build our breadcumb
return this.buildBreadCrumb(route.firstChild, nextUrl, newBreadcrumbs);
}
if (newBreadcrumbs) {
return newBreadcrumbs;
} else {
return [];
}
}
toggleCollapseBreadcrumb(){
this.collapsed = !this.collapsed;
}
}
export interface Breadcrumb {
label: string;
url: string;
icon: string;
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BreadcrumbsComponent } from "./breadcrumbs.component";
import {MatIconModule} from '@angular/material/icon';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [
BreadcrumbsComponent
],
imports: [
CommonModule,
RouterModule,
MatIconModule
],
exports: [
BreadcrumbsComponent
]
})
export class BreadcrumbsModule { }