Angular Router set dynamically document Title of page, Protect Router with guard, and Breadcrumbs component
Tags: angular, typescript, javascript, breadcrumbs
Published: 2020-10-31 10:26:45

In this article, we are going to understand proper way...

1. How one can use angular router ?
2. How to protect angular router ?
3. How to set dynamic document title when route page changes ?
4. 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.

```javascript 
   ng new dynamictitle
```
open `app-routing.module.ts` file, and adds routing path.

```javascript

  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.

1. 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. 

2. 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.

3. 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..

```javascript 
   ng generate component Main
```
In above command just change for other components like..
Home, About, breadcrums, header, Login. 

Now, generate child component of HomeComponent..

```javascript
   ng g c home/records
```
and inside records component generate find records component..

```javascript
   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.

```javascript 
   ng g s authentication
   ng g guard protect-guard
```
Open authentication service...

```javascript
   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..

```javascript
   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

```javascript
     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..

```javascript
   ng g s main
```
Then, inside main service...

```javascript
   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

```javascript
   <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
```javascript
   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<IUrlTitle> and subscribe it inside component using async pipe. then inside template file we pass input to breadcrums component.

Now, generate breadcrums.template file

```javascript 
   <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
```javascript
   export class BreadcrumsComponent implements OnInit {
  items;
  @Input("modelItems") set menu(item) {
    this.items = item;
  }
  constructor() {}

  ngOnInit() {}
}
```
**Complete working example is here**

{% stackblitz angular-ivy-upbpqy %}