Build Navigation in Angular with Router, RouterLink and Kendo UI

Using Router, RouterLink & RouteActive for Navigation in Angular

ยท

11 min read

When we build an application in Angular, it works as SPA so it is a single page with multiple views or pages running in the client. But how can we navigate between each one without a new request to the server?

The Angular router updates the address bar and loads views or pages without refreshing, linking a URL or path to a component to load. It does more than just updating the address bar and loading associated components.

The router helps to pass data between components, protects views with guards, combines or nests routes, helps with lazy loading, or works a state for our app.

I believe the best way to learn about the router is by building something, where we play with it, so let's do it!

Setup The Project

Before we begin, we will create an app using the latest version of Angular CLI.

ng new play-with-router

Navigate to the play-with-router directory and run ng serve command.

Click on the link and it's open our sandbox to play with the router.

The Router

Because we are a standalone app generated by Angular, our application starts by using the main.ts.

the main.ts contains the bootstrapApplication function, with the start point component in our case AppComponent and the app.config.ts.

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));

The app.config is an object with the providers section, to provide functions to register dependencies for our app. In our case, we use the provideRouter function, which expects an array of routes for our app and a list of extra optional parameters.

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};

By default, the CLI generates the app.routes.ts with an empty ROUTES array, ready for declaration.

export const routes: Routes = [];

Before Angular 16, we used the RouterModule to provide and register our routes and give acesss to directives, services and types.

Declare Route

Before declaring our routes, we need to create the following list of components home, about, and not-found in the pages directory using the Angular CLI.

$ ng g c pages/home
CREATE src/app/pages/home/home.component.html (20 bytes)
CREATE src/app/pages/home/home.component.spec.ts (605 bytes)
CREATE src/app/pages/home/home.component.ts (239 bytes)
CREATE src/app/pages/home/home.component.scss (0 bytes)

Next, open the app.routes.ts and declare the first route in the routes array. The Route object has a bunch of properties, but for our case, we only set up path and component.

Import the component for each route, the path represents the path in the URL:

  • path: '' : The main path like localhost:4200

  • path:'about' : about path like localhost:4200/about

  • path: '**': works like a wildcard to any route like http://localhost:4200/wherever-place

The final code looks like this:

import { Routes } from '@angular/router';
import {HomeComponent} from "./pages/home/home.component";
import {AboutComponent} from "./pages/about/about.component";
import {NotFoundComponent} from "./pages/not-found/not-found.component";

export const routes: Routes = [
  {
    path:'',
    component: HomeComponent,
  },
  {
    path:'about',
    component: AboutComponent
  },
  {
    path: '**',
    component: NotFoundComponent
  }
];

Note: by default Angular router use first-match strategy: prefix, so must to be from specific route to less and use wildcard at the end.

  • prefix:, it's find the parth and try with child routes.

  • full: all segments must match the path skipping the child.

Read more about pathMatch

Open the app.component.html, which contains default HTML and a routerOutlet. Remove the existing content and add the following snippet:

<header>
  <h1>The Store</h1>
</header>
<section>
  <router-outlet />
</section>

Save the changes and run ng server -o to open the browser to the local server. To test our routes, navigate to each route, and the router loads each component that matches. Additionally, the wildcard captures any incorrect routes, redirecting them to the not-found component.

it works but what happens when we want to add more routes or under the about like about/team or about/company? it's time to use children's routes.

Children Routes

We want to create new routes, so using the CLI generate two pages more team and company under the about page.

ng g c pages/about/company
ng g c pages/about/team

Next, under the about directory, create the about.route.ts file to declare the new routes. This will be similar to the initial app.routes, but in our case, it is a single object with the children property pointing to the new routes about/team and about/company, as shown in the following code:

export const aboutRoutes: Route =
  {
    path:'about',
    component: AboutComponent,
    children: [
      {
        path: 'team',
        component: TeamComponent
      },
      {
        path: 'company',
        component: CompanyComponent
      }
    ]
  }

Next, add the <router-oulet> to the about.component.html

<p>about works!</p>
<router-outlet/>

Finally, in the app.routes, import the aboutRoutes into the routes array.

import { Routes } from '@angular/router';
import {HomeComponent} from "./pages/home/home.component";
import {NotFoundComponent} from "./pages/not-found/not-found.component";
import {aboutRoutes} from "./pages/about/about.routes";

export const routes: Routes = [
  {
    path:'',
    component: HomeComponent,
  },
 aboutRoutes,
  {
    path: '**',
    component: NotFoundComponent
  }
];

Save changes and navigate to the new routes!

Route Tokens

We have defined our router path in a static string, and it works, but what happens if we need to declare the path in other places like navigation, and then we want to change the path name from home to start?

We must change the string 'home' in all places. Instead of adding the route manually, declare a const with our route tokens.

Open the app.route.ts and declare a const with all route tokens, something like:

export const ROUTE_TOKENS = {
  HOME : 'home',
  ABOUT : 'about',
  COMPANY : 'company',
  TEAM : 'team'
}

Next, update the app.route.ts and about.route.ts to use the ROUTE_TOKENS.

app.route.ts file

export const ROUTE_TOKENS = {
  HOME : 'home',
  ABOUT : 'about',
  COMPANY : 'company',
  TEAM : 'team'
}

export const routes: Routes = [
  {
    path:'',
    redirectTo: ROUTE_TOKENS.HOME,
    pathMatch: "full"
  },
  {
    path: ROUTE_TOKENS.HOME,
    component: HomeComponent,
  },
 aboutRoutes,
  {
    path: '**',
    component: NotFoundComponent
  }
];

about.route.ts

import {Route} from "@angular/router";
import {AboutComponent} from "./about.component";
import {TeamComponent} from "./team/team.component";
import {CompanyComponent} from "./company/company.component";
import {ROUTE_TOKENS} from "../../app.routes";

export const aboutRoutes: Route =
  {
    path:ROUTE_TOKENS.ABOUT,
    component: AboutComponent,
    children: [
      {
        path: ROUTE_TOKENS.ABOUT,
        component: TeamComponent
      },
      {
        path: ROUTE_TOKENS.COMPANY,
        component: CompanyComponent
      }
    ]
  }

With this small refactor, we can create the navigation without any issues regarding typos or difficulty updating the path.

Create The Navigation

We've set up the router, but providing navigation links is much more convenient than typing the path every time we want to go somewhere.

Angular offers several methods to create powerful navigation using the RouterLink and RouterLinkActive directives, as well as the RouterService, for a robust and user-friendly experience.

The RouterLink directive helps us connect our HTML elements to communicate with the router, we just import it in the imports array.

The routerLink is more than set the path to the route, it helps to pass parameters like string or dynamic values to the path segment.

  • "/" for an absolute path

  • "./" relative to the current active route path

  • "" without a slash adds the path to the current active route

  • "../" moves up one level from the current active route

Read more about Relative Paths

Create a Navigation

We want to create an area that includes all routes. The easiest way to do this is by creating a navigation component. To create a new component, use the Angular CLI:

ng g c components/navigation
CREATE src/app/components/navigation/navigation.component.html (26 bytes)
CREATE src/app/components/navigation/navigation.component.spec.ts (647 bytes)
CREATE src/app/components/navigation/navigation.component.ts (263 bytes)
CREATE src/app/components/navigation/navigation.component.scss (0 bytes)

First, we need to import the directives RouterLink , RouterActive and the ROUTE_TOKENS in the navigation.component.ts

import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
import { ROUTE_TOKENS } from '../../route-token';

@Component({
  selector: 'app-navigation',
  standalone: true,
  imports: [RouterLink, RouterLinkActive],
  templateUrl: './navigation.component.html',
  styleUrl: './navigation.component.scss',
})
export class NavigationComponent {
  public readonly navigation = ROUTE_TOKENS;
}

Open navigation.component.html file markup using the ul and li with the routeLink directive and passing the token HOME, and ABOUT.

<ul>
  <li [routerLink]="navigation.HOME">Home</li>
  <li [routerLink]="navigation.ABOUT">About</li>
</ul>

Remember to add links to the company and team page in the about page. First, import the ROUTE_TOKENS in the about.component.ts.

import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
import { ROUTE_TOKENS } from '../../route-token';

@Component({
  selector: 'app-about',
  standalone: true,
  imports: [RouterOutlet, RouterLink],
  templateUrl: './about.component.html',
  styleUrl: './about.component.scss',
})
export class AboutComponent {
  protected readonly ROUTE_TOKENS = ROUTE_TOKENS;
}

Finally, add two links using the routerLink directive pointing to the COMPANY and TEAM tokens.

<p>about works!</p>
Thanks for interest in learn more about
  <a [routerLink]="[ROUTE_TOKENS.COMPANY]">
    Company
  </a> and our
  <a [routerLink]="[ROUTE_TOKENS.TEAM]">
    TEAM
  </a>

<router-outlet></router-outlet>

Save changes and we have a navigation in our application! ๐ŸŽ‰

Alright, we now have navigation without the need for manual intervention. However, it looks a bit plain. How can we improve the navigation without putting in too much effort?

Building a Navigation with Kendo NavBar

First, install the Kendo UI Navigation using the schematics, as it registers all the dependencies required by Kendo UI.

ng add @progress/kendo-angular-navigation

Because I love the fluent theme, I will install it and update the angular.json to use it.

npm install --save @progress/kendo-theme-fluent

Remmeber to add the font-family: "Segoe UI", sans-serif in styles.scss to use Segoe UI as default font.

Import the NavigationModule in the navigation.component.ts, then open the navigation.component.html to modify the markup.

To set up my navigation, I utilize several components provided by the NavigationModule.

  • kendo-appbar container for app bar with position top and theme dark.

  • kendo-appbar-spacer: space between elements.

  • kendo-appbar-section: set a section one for the title and the other for the list of links.

To visually show the active route to the user, we combine the routerLinkActive directive with a new class active-link in this way [routerLinkActive]="'active-link'".

The final code looks like this:

<kendo-appbar position="top" themeColor="dark">
  <kendo-appbar-spacer width="20px"></kendo-appbar-spacer>
  <kendo-appbar-section>
    <h1 class="title">The Store</h1>
  </kendo-appbar-section>
  <kendo-appbar-spacer width="42px"></kendo-appbar-spacer>
  <kendo-appbar-section>
    <ul>
      <li
        [routerLinkActive]="'active-link'"
        [routerLink]="navigation.HOME">Home</li>
      <li
        [routerLinkActive]="'active-link'"
        [routerLink]="navigation.ABOUT">About</li>
    </ul>
  </kendo-appbar-section>
</kendo-appbar>

Add the following snippet to navigation.component.scss, making adjustments to the ul and modifying the color and font-weight for the active link class.

ul {
  margin: 0;
  display: flex;
  list-style-type: none;
  padding: 0;
  li {
    margin: 0 9px;
  }
  li:hover {
    cursor: pointer;
    color: #e87890;
    font-weight: bold;
  }
}
.active-link {
  color: #e87890;
  font-weight: bold;
}

Before finishing, add some content to the home and about pages to make them look less empty.

home.component.html

<h2>Welcome to the Store!</h2>
<p>Discover quality products, unbeatable prices, and exceptional service.
  From fashion to electronics, we've got what you need. Shop with confidence and enjoy a hassle-free experience with us.
</p>

about.component.html

<h2>About</h2>

<p>Welcome to our About page! At The Store, we're more than just an online store โ€“ we're a passionate team dedicated to
revolutionizing the way you shop. Our journey began with a simple idea: to create a platform where customers can discover
unique products from around the globe, all in one convenient location. With a focus on quality, innovation, and customer
satisfaction, we've built a brand that stands for excellence in every aspect of the shopping experience.</p>
<p>

<p>Behind The Store is a diverse and talented team committed to bringing our vision to life. From our dedicated customer
support specialists to our expert product curators, each member plays a crucial role in delivering the exceptional
service and selection that our customers know and love. Meet the faces behind the brand and learn more about our team.</p>

Save changes and observe the improved navigation with status indicators when a user selects a route! yeah!! ๐ŸŽ‰

Using the navigation component is great because users can interact with it. However, what if we want to navigate based on certain conditions, such as when a user accepts some terms, or if something is valid, we send the user to the home page or recovery?

For this purpose, Angular offers the Router Service. Let's check it out!

Navigate with Router Service

The RouterService provides a set of methods and functionality, but one of them is methods navigate and navigateByUrl , which allow to move the user through the application and routes programmatically.

  • navigate: works with absolute and relative paths, and also supports the NavigationExtras object for query params, passing state, or managing browser history.

  • navigateByUrl: only supports absolute paths, with a string URL or URL tree, and supports optional NavigationBehaviorOptions for state and browser history.

In our example, we use the navigate method, in the home component we want when accept some terms them navigate to the about page, by using the router service.

Open the home.component.ts and inject the router. Then, create a handler for acceptTerms. If it is true, navigate to the about URL.

export class HomeComponent {
  readonly router = inject(Router);
  public acceptTerms(value: boolean) {
    if (value) {
      this.router.navigate([ROUTE_TOKENS.ABOUT]);
    }
  }
}

In the home.component.html, add the following snippet, which includes text and a checkbox. Bind the change event to the acceptTerms method, passing the accepterm checkbox value:

<label>
  Do you want to see the about page ?
<input #accepterm type="checkbox" (change)="acceptTerms(accepterm.checked)" >
</label>

Save changes and see how the user navigates from home to about when accept the terms ๐Ÿฅฐ

Recap

Yes, we learned how to register routes within our application, making it easier to define the paths that users can follow. Additionally, we used the routerLink and routeActiveLink directives, which allowed us to create seamless navigation between different components and receive visual feedback.

Additionally, we can build visually appealing and user-friendly navigation using Kendo UI Navigation and the RouterService. This provides us with the ability to programmatically navigate to routes from within our code.

ย