Skip to content

Programmatic Navigation and Route Guards

Not all navigation happens via [routerLink] in templates. Sometimes you need to navigate in response to code — after a form submission, after a timeout, or when a condition is met. Angular’s Router service provides programmatic navigation. Route guards let you intercept navigation to protect routes.

Inject the Router service and call navigate():

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
@Component({
standalone: true,
imports: [FormsModule],
template: `
<input [(ngModel)]="query" (keydown.enter)="search()" placeholder="Search..." />
`
})
export class NavBar {
query = '';
constructor(private router: Router) {}
search(): void {
if (this.query.trim()) {
this.router.navigate(['/search'], { queryParams: { q: this.query } });
this.query = '';
}
}
}

navigate() accepts the same arguments as [routerLink]: an array of path segments and an optional options object with queryParams, fragment, and other settings.

A route guard is a function that runs before a route is activated. It returns true to allow navigation, false to block it, or a UrlTree to redirect elsewhere.

In Angular 15+, guards are plain functions (not classes), which makes them simple to write and test:

guards/watchlist-guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { WatchlistService } from '../services/watchlist.service';
import { ToastService } from '../services/toast.service';
export const watchlistGuard: CanActivateFn = () => {
const watchlist = inject(WatchlistService);
const router = inject(Router);
const toast = inject(ToastService);
if (watchlist.count() > 0) {
return true;
}
toast.show('Add a movie to your watchlist first.');
return router.createUrlTree(['/browse']);
};

Register the guard on the route:

{ path: 'watchlist', component: Watchlist, canActivate: [watchlistGuard] }

When a user navigates to /watchlist:

  1. The guard runs
  2. If the watchlist is empty, it shows a toast notification and redirects to /browse
  3. If the watchlist has items, it returns true and the Watchlist component renders

This example demonstrates something important: guards are not limited to binary allow/deny decisions. The watchlistGuard shows a toast message before redirecting. Guards can call services, log analytics, check auth tokens, fetch data — anything that needs to happen before a route activates.

Notice that the guard uses inject() instead of constructor injection. Since the guard is a function (not a class), there is no constructor. The inject() function is Angular’s alternative — it retrieves a service from the DI system and can be called at the top level of a function that runs inside an injection context (such as guard execution).

router.createUrlTree(['/browse']) creates a UrlTree object representing the /browse URL. Returning a UrlTree from a guard causes Angular to redirect to that URL instead of activating the original route.

  1. Create a WatchlistGuard in your practice project that checks a hasItems flag.
  2. If hasItems is false, log a message and redirect to '/'.
  3. If hasItems is true, return true.
  4. Apply the guard to a /protected route.
  5. Navigate to /protected with hasItems = false and confirm you are redirected. Set hasItems = true and confirm you can access the route.
  • Router.navigate(['/path']) navigates programmatically — useful after form submissions, timeouts, or conditional logic.
  • CanActivateFn guards are plain functions that run before a route activates.
  • Return true to allow, false to block, or router.createUrlTree(['/path']) to redirect.
  • Guards can inject services with inject() and trigger side effects before making their decision.
  • Register guards on routes with canActivate: [myGuard].