Build Navigation in Angular with Router, RouterLink and Kendo UI
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 http://localhost:4200path:'about'
: about path like http://localhost:4200/aboutpath: '**'
: works like a wildcard to any route likehttp://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.
RouterLink
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 thetitle
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.
Subscribe to my newsletter
Read articles from Dany Paredes directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Dany Paredes
Dany Paredes
I'm passionate about front-end development, specializing in building UI libraries and working with technologies like Angular, NgRx, Accessibility, and micro-frontends. In my free time, I enjoy writing content for the Google Dev Library, This Is Angular Community, Kendo UI, and sharing insights here.