Learn Angular - Routing & Navigation
Episode 9 of 24

Learn Angular - Routing & Navigation

This episode covers Angular routing and navigation: Angular Router basics, route configuration with child routes and lazy loading, protecting routes with route guards and resolvers, and managing query params, fragments, and navigation extras.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Real applications consist of many pages: home, product list, detail, login. The Angular Router manages movement between these pages — including the URL, state, and access protection.

Episode 9 covers Angular Router basics, route configuration with child routes and lazy loading, route guards and resolvers, and query params, fragments, and navigation extras. Good routing makes an application feel whole and navigation between features smooth.

Angular Router Basics

Setting Up the Router

The router is enabled via provideRouter in app.config.ts, then the root component loads the active page through <router-outlet>.

JSBasic route configuration
import { provideRouter, Routes } from '@angular/router';
import { BerandaComponent } from './beranda/beranda.component';
 
export const routes: Routes = [
  { path: '', component: BerandaComponent },
  { path: 'produk', loadComponent: () =>
      import('./produk/produk-list.component').then((m) => m.ProdukListComponent) },
  { path: 'detail/:id', component: DetailComponent },
  { path: '**', redirectTo: '' },
];

The '' route is the default page, detail/:id uses a route parameter, and '**' is the wildcard for unknown pages. loadComponent makes the route lazy-loaded — the component's code is only downloaded when the route is accessed.

Navigation is done with RouterLink in the template or Router in the class:

HTMLNavigation with RouterLink
<a routerLink="/produk" routerLinkActive="aktif">Daftar Produk</a>
<a routerLink="/detail/42">Buka Detail</a>

routerLinkActive="aktif" adds a class while the link is active. From the class, use router.navigate(['/detail', id]) or router.navigateByUrl('/produk') for programmatic navigation, for example after login.

Route Configuration, Child Routes, and Lazy Loading

Child Routes

Pages that have sub-pages — for example an admin area with dashboard, user, and settings — are arranged with child routes:

JSChild routes with an admin layout
export const routes: Routes = [
  {
    path: 'admin',
    loadComponent: () => import('./admin/admin-layout.component')
      .then((m) => m.AdminLayoutComponent),
    children: [
      { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
      { path: 'dashboard', loadComponent: () =>
          import('./admin/dashboard.component').then((m) => m.DashboardComponent) },
      { path: 'user', loadComponent: () =>
          import('./admin/user.component').then((m) => m.UserComponent) },
    ],
  },
];

AdminLayoutComponent renders the admin menu plus a <router-outlet> to host child pages. With loadComponent at every level, the bundle is split per feature, so the initial page is much lighter.

The Benefits of Lazy Loading

Lazy loading splits the application into small chunks that are downloaded on demand. The results: faster initial load, pages rarely accessed don't burden the user, and the separation of responsibilities per feature becomes clearer.

Route Guards and Resolvers

An Auth Guard with CanActivateFn

Guards control who may access a route. The modern form is a CanActivateFn function with inject:

JSA functional auth guard
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
 
export const authGuard: CanActivateFn = () => {
  const auth = inject(AuthService);
  const router = inject(Router);
 
  if (auth.isLoggedIn()) {
    return true;
  }
  return router.createUrlTree(['/login'], { queryParams: { kembali: router.url } });
};

authGuard returns true if the user is logged in, or a login URL with the kembali query param so the user can be returned to the original page after login. The guard is attached to a route: { path: 'profil', component: ProfilComponent, canActivate: [authGuard] }.

Resolvers to Prepare Data

A resolver fetches data before the component renders:

JSResolver for product data
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { ProdukService } from './produk.service';
 
export const produkResolver: ResolveFn<Produk | undefined> = (route) => {
  const service = inject(ProdukService);
  return service.cari(Number(route.paramMap.get('id')));
};

produkResolver is called before the DetailComponent activates, so the data is already available at initial render. This avoids the flickering "loading" state on detail pages.

Query Params, Fragments, and Navigation Extras

Reading and Sending Query Params

Query params hold URL state such as search and filters:

JSReading query params
import { inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
 
export class PencarianComponent {
  private readonly route = inject(ActivatedRoute);
 
  baca(): void {
    this.route.queryParams.subscribe((params) => {
      const q = params['q'] ?? '';
      this.mulaiPencarian(q);
    });
  }
}

To send a query param while navigating, router.navigate(['/pencarian'], { queryParams: { q: 'angular' } }). Since queryParams is an Observable, the component can react every time the user changes a filter.

Fragments and Navigation Extras

A fragment is the part of a URL after #, usually for page anchors. Navigating with a fragment: router.navigate(['/dokumentasi'], { fragment: 'instalasi' }). Other useful navigation extras: state to send invisible data to the destination page, replaceUrl so the history doesn't pile up, and preserveQueryParams to keep query params when changing pages.

Wrap Up

Key takeaways:

  • provideRouter(routes) enables the Angular Router; <router-outlet> displays the active page.
  • Route parameters are marked with a colon, like detail/:id.
  • loadComponent and child routes enable per-feature lazy loading.
  • Functional guards with CanActivateFn protect routes from unauthorized access.
  • Resolvers prepare data before a component renders.
  • Query params, fragments, and navigation extras enrich URL state and navigation.

In the next episode, episode 10, we'll cover state management and reactive patterns — managing application state with RxJS patterns, using BehaviorSubject and ReplaySubject as state services, getting to know NgRx or Akita, and managing side effects in a structured way.