In this article, we are going to understand proper way...
- How one can use angular router ?
- How to protect angular router ?
- How to set dynamic document title when route page changes ?
- How to make Breadcrumbs component ?
In this project we are using Bootstrap and here I am not setting up content of bootstrap, I am assuming you have pre set up bootstrap component in angular project.
First of all create new angular project with routing option.
ng new dynamictitle
open app-routing.module.ts
file, and adds routing path.
const routes: Routes = [
{
path: "",
component: MainComponent,
data: { title: "Root Page", breadcrums: "Root" }
},
{
path: "login",
component: LoginComponent,
data: { title: "Login Page", breadcrums: "Login" }
},
{
path: "home",
component: HomeComponent,
data: { title: "Home Page", breadcrums: "Home" },
children: [
{
path: "records",
component: RecordsComponent,
data: { title: "Home / Records Page", breadcrums: "Records" },
children: [
{
path: "findrecords",
component: HelloComponent,
data: { title: "Find Records Page", breadcrums: "Find-Records" }
}
]
}
],
canActivate: [ProtectRouteGuard]
},
{
path: "about",
component: AboutComponent,
data: { title: "About Page", breadcrums: "About" },
canActivate: [ProtectRouteGuard]
}
];
Now, we are adding some new properties above router file.
data : { title: '', breadcrumbs: '' }, data object have two property, later in component tree we need this information when that route in changed. title is displaying over document title, and breadcrumbs are displaying inside root page.
canActivate: [] , canActivate router guard protects our path, and we can not routed to this page without login to site or any restriction we are imposing based on user role.
children: [], this is used to set inner path of page. so this are inner routed pages or we can say this routes are children of related parent. like
Home => Records => Find-Records
.
as per route path configuration, create related component using..
ng generate component Main
In above command just change for other components like..
Home, About, breadcrums, header, Login.
Now, generate child component of HomeComponent..
ng g c home/records
and inside records component generate find records component..
ng g c home/records/hello
Now, we are protecting our path..
for that generate new service and guard inside authentication folder, if not then create new root level authentication folder. inside that folder generate service and guard.
ng g s authentication
ng g guard protect-guard
Open authentication service...
export class AuthenticationService {
private isLoggedIn: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(
false
);
private isLoggedIn$ = this.isLoggedIn.asObservable();
constructor(private router: Router) {}
getIsUserLoggedIn(): Observable<boolean> {
return this.isLoggedIn$;
}
setUserLoggedIn(loggedInStatus: boolean) {
this.isLoggedIn.next(loggedInStatus);
loggedInStatus
? this.router.navigate(["home"])
: this.router.navigate(["login"]);
}
}
and inside auth-guard..
export class ProtectRouteGuard implements CanActivate {
constructor(
private authService: AuthenticationService,
private router: Router
) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return this.authService.getIsUserLoggedIn().pipe(
take(1),
map((isLoggedIn: boolean) => {
if (!isLoggedIn) {
this.router.navigate(["login"]);
return false;
}
return true;
})
);
}
}
Now we provide protected-guard services inside app.module.ts
providers: [ProtectRouteGuard]
and inside app-routing.module.ts file we protect route using canActivate: [ProtectRouteGuard]
Upto now, we are done protecting our routes, now we moove to second part series.
PART 2 [SETTING DYNAMIC TITLE PATH, and Breadcrums]
First, generate new main service..
ng g s main
Then, inside main service...
export class MainService {
routerEventsTitle$: Observable<IUrlTitle>;
breadCrumbs: IBreadCrums[] = [{ label: "", url: "" }];
constructor(
private title: GetSetPageTitleService,
private router: Router,
private activatedRouter: ActivatedRoute
) {
this.routerEventsTitle$ = this.getSetRouterTitle();
}
setDefaultTitle(defaultTitle: string) {
this.title.setCurrentTitle(defaultTitle);
}
getSetRouterTitle(): Observable<IUrlTitle> {
return this.router.events.pipe(
filter((event: RouterEvent) => event instanceof NavigationEnd),
map((routeUrl: RouterEvent) => {
let childRouter = this.activatedRouter.firstChild;
while (childRouter.firstChild) {
childRouter = childRouter.firstChild;
}
if (childRouter.snapshot.data["title"]) {
let titleBreadCrums: IUrlTitle = {
url: routeUrl.url,
title: childRouter.snapshot.data["title"]
};
return titleBreadCrums;
}
return {
url: routeUrl.url,
title: this.title.getCurrentTitle()
};
}),
map((titleUrl: IUrlTitle) => {
this.breadCrumbs.length = 0;
let menuItem = this.generateBreadCrums(this.activatedRouter.root);
this.breadCrumbs.push(...menuItem);
return { ...titleUrl, breadCrums: this.breadCrumbs };
}),
tap((currentTitle: IUrlTitle) => {
// /this.breadCrumbs.push(currentTitle);
this.title.setCurrentTitle(currentTitle.title);
console.log("b ", this.breadCrumbs);
})
);
}
generateBreadCrums(
activatedRouter: ActivatedRoute,
url = "",
breadcrumbs: IBreadCrums[] = [{ label: "", url: "" }]
): IBreadCrums[] {
const children: ActivatedRoute[] = activatedRouter.children;
if (children.length === 0) {
return breadcrumbs;
}
for (const child of children) {
const routeURL: string = child.snapshot.url
.map(segment => segment.path)
.join("/");
if (routeURL !== "") {
url += `/${routeURL}`;
}
console.log("url ", routeURL);
const label = child.snapshot.data["breadcrums"];
console.log("label ", label);
if (label) {
breadcrumbs.push({ label, url });
}
return this.generateBreadCrums(child, url, breadcrumbs);
}
}
}
Now, Open app.component.html file
<ng-container *ngIf="routerEventsTitle$| async as routerTitle">
<app-header> </app-header>
<app-breadcrums [modelItems]="routerTitle.breadCrums"> </app-breadcrums>
<div class="container">
<router-outlet></router-outlet>
</div>
</ng-container>
Now, Open app.component.ts file
export class AppComponent {
name = "Angular " + VERSION.major;
appRootTitle = "Root Page";
routerEventsTitle$: Observable<IUrlTitle>;
constructor(private mainService: MainService) {
this.mainService.setDefaultTitle(this.appRootTitle);
}
ngOnInit() {
this.routerEventsTitle$ = this.mainService.routerEventsTitle$;
}
}
First of all we inject main-service inside app.component.ts, then we gets Observable and subscribe it inside component using async pipe. then inside template file we pass input to breadcrums component.
Now, generate breadcrums.template file
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
Your are at :
<li *ngFor="let bc of items; let last = last" class="breadcrumb-item" aria-current="page" [class.active]="last">
<a *ngIf="last !==true" [routerLink]="bc.url"> {{bc.label}} </a>
<span *ngIf="last" > {{bc.label}} </span>
</li>
</ol>
</nav>
Now, generate breadcrums.ts file
export class BreadcrumsComponent implements OnInit {
items;
@Input("modelItems") set menu(item) {
this.items = item;
}
constructor() {}
ngOnInit() {}
}
Complete working example is here
Top comments (0)