In this tutorial, we’re gonna look at way to add/remove Route dynamically displaying with Navigation Bar.
Example Overview
Our App has a Navbar that shows Home and Login page at the beginning:
Right after logging in as a User or an Admin, the Navbar changes:
– remove Login page
– add User Board/Admin Board page
– add Logout button
Clicking on Logout button will change the Navbar to the beginning UI:
How to add/remove Route dynamically
Define Route
We define static Routes that will be display all the time. In this example, it’s the Route for Home page.
app-routing.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: 'home', component: HomeComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } |
Configure Components for dynamic Routes
Routes to LoginComponent
, AdminComponent
and UserComponent
will be dynamically add/remove into Navbar, so we define in entryComponents
list so that these Components can be dynamically loaded into the view.
app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import { AppRoutingModule } from './app-routing.module'; @NgModule({ declarations: [ ... ], imports: [ ... AppRoutingModule ], entryComponents: [ LoginComponent, UserComponent, AdminComponent ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } |
Add dynamic Routes
For Dynamic Routes that haven’t been defined in app-routing.module.ts, we add Route with necessary data ({path,component}
) to config
array using unshift()
method (or push()
method for the end of the Route[]
array):
1 2 3 4 5 6 7 |
constructor(private router: Router) { this.router.config.unshift( { path: 'login', component: LoginComponent }, { path: 'user', component: UserComponent }, { path: 'admin', component: AdminComponent }, ); } |
We need a list of available links to be modified in the future, so we declare an Array:
1 |
links: Array<{ text: string, path: string }>; |
Then show the links
Array on the template:
1 2 3 4 5 6 |
<ul class="nav navbar-nav" routerLinkActive="active"> ... <li class="nav-item" *ngFor="let link of links"> <a class="nav-link" [routerLink]="link.path">{{ link.text }}</a> </li> </ul> |
Add/Remove Route dynamically
Now our work is simple. We just add/remove link to the links Array that we have created before:
1 2 3 4 5 6 7 8 9 |
// add new link this.links.push({ text: 'Admin Board', path: 'admin' }); // remove existent link this.links.forEach((link, index) => { if (link.text === 'Admin Board') { this.links.splice(index, 1); } }); |
Practice
Project Structure
– define Routes
in app-routing.module.ts
– 4 page Components for Routes: home, login, user, admin
– implement a service for manage Navbar items(links) and login status in navbar.service.ts
– config page Components for Routes in app.module.ts
– app.component.ts initializes dynamic Routes and app.component.html contains template for displaying dynamic Navbar.
Setup Project
– Create Angular Project:
ng new AngularDynamicRoutes
– Generate Page Components:
ng g c home
ng g c login
ng g c admin
ng g c user
Define Routes
app-routing.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home/home.component'; const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: 'home', component: HomeComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } |
Config Components for dynamic Routes
app.module.ts
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 |
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { UserComponent } from './user/user.component'; import { AdminComponent } from './admin/admin.component'; import { LoginComponent } from './login/login.component'; @NgModule({ declarations: [ AppComponent, HomeComponent, UserComponent, AdminComponent, LoginComponent ], imports: [ BrowserModule, AppRoutingModule ], entryComponents: [ LoginComponent, UserComponent, AdminComponent ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } |
Run command: ng g s services/navbar
.
navbar.service.ts
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 |
import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class NavbarService { private links = new Array<{ text: string, path: string }>(); private isLoggedIn = new Subject<boolean>(); constructor() { this.addItem({ text: 'Login', path: 'login' }); this.isLoggedIn.next(false); } getLinks() { return this.links; } getLoginStatus() { return this.isLoggedIn; } updateLoginStatus(status: boolean) { this.isLoggedIn.next(status); if (!status) { this.clearAllItems(); this.addItem({ text: 'Login', path: 'login' }); } } updateNavAfterAuth(role: string): void { this.removeItem({ text: 'Login' }); if (role === 'user') { this.addItem({ text: 'User Board', path: 'user' }); } else if (role === 'admin') { this.addItem({ text: 'Admin Board', path: 'admin' }); } } addItem({ text, path }) { this.links.push({ text: text, path: path }); } removeItem({ text }) { this.links.forEach((link, index) => { if (link.text === text) { this.links.splice(index, 1); } }); } clearAllItems() { this.links.length = 0; } } |
app.component.ts
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 |
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { UserComponent } from './user/user.component'; import { AdminComponent } from './admin/admin.component'; import { LoginComponent } from './login/login.component'; import { NavbarService } from './services/navbar.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { links: Array<{ text: string, path: string }>; isLoggedIn = false; constructor(private router: Router, private navbarService: NavbarService) { this.router.config.unshift( { path: 'login', component: LoginComponent }, { path: 'user', component: UserComponent }, { path: 'admin', component: AdminComponent }, ); } ngOnInit() { this.links = this.navbarService.getLinks(); this.navbarService.getLoginStatus().subscribe(status => this.isLoggedIn = status); } logout() { this.navbarService.updateLoginStatus(false); this.router.navigate(['home']); } } |
app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<nav class="navbar navbar-inverse"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">grokonez</a> </div> <ul class="nav navbar-nav" routerLinkActive="active"> <li class="nav-item"><a class="nav-link" routerLink="home">Home</a></li> <li class="nav-item" *ngFor="let link of links"> <a class="nav-link" [routerLink]="link.path">{{ link.text }}</a> </li> <li class="nav-item" *ngIf="isLoggedIn"> <a class="nav-link" routerLink="logout" (click)="logout()">Logout</a> </li> </ul> </div> </nav> <div class="container"> <router-outlet></router-outlet> </div> |
Login Components
login.component.ts
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 |
import { Component, OnInit } from '@angular/core'; import { NavbarService } from '../services/navbar.service'; @Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { isLoggedIn = false; role = ''; constructor(private navbarService: NavbarService) { this.navbarService.getLoginStatus().subscribe(status => this.isLoggedIn = status); } ngOnInit() { } loginUser() { this.navbarService.updateNavAfterAuth('user'); this.navbarService.updateLoginStatus(true); this.role = 'user'; } loginAdmin() { this.navbarService.updateNavAfterAuth('admin'); this.navbarService.updateLoginStatus(true); this.role = 'admin'; } } |
login.component.html
1 2 3 4 5 6 7 8 9 10 11 |
<div *ngIf="isLoggedIn; else loggedOut"> Logged in as {{role}}. </div> <ng-template #loggedOut> <button class="btn btn-secondary" (click)="loginUser()">Login as User</button> <button class="btn btn-primary" (click)="loginAdmin()">Login as Admin</button> <hr /> <p>Don't have an account?</p> <a href="signup" class="btn btn-success">Sign Up</a> </ng-template> |
I don’t understand why you need dynamic routing for this? Can’t you just hide/unhide whatever menu items you need based on login status?
Your components aren’t dynamic, you may as well declare them all in your app routing module?
Can you show an example which dynamically loads ‘About’, ‘Privacy’, ‘Terms’ page content e.g. from a database, using the same component?
Hi, you can simply do this with
*ngIf
π , the example is in this tutorial:Angular Spring Boot JWT Authentication example | Angular 6 + Spring Security + MySQL Full Stack β Part 3: Build Frontend
this is the thing i was looking for so long . Thankyou