Theory
Smarter navigation and shared state
Basic routing maps a URL to a component. Real apps need more: load heavy pages only when visited, stop unauthorised users reaching a page, warn before leaving a half-filled form, and have data ready the moment a page appears. And components across the app often need to share the same changing state, like who is logged in.
Angular provides four tools for this: lazy loading, route guards, resolvers, and RxJS Subjects. This lesson introduces each, so FestConnect can navigate intelligently and keep its state in sync.
At a glance
| Tool | What it does |
|---|---|
| Lazy loading | Loads a route's code only when the user navigates there (smaller initial load) |
| CanActivate guard | Decides whether a route may be ENTERED (e.g. must be logged in) |
| CanDeactivate guard | Decides whether the user may LEAVE (e.g. warn about unsaved changes) |
| Resolver | Fetches data BEFORE the route activates, so the page has it ready |
| RxJS Subject / BehaviorSubject | Shares changing state across components (BehaviorSubject also holds a current value) |
Practical
Lazy loading and a functional route guard
import { Routes } from '@angular/router';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
// A functional CanActivate guard (Angular 17 style)
const authGuard = () => inject(AuthService).isLoggedIn();
export const routes: Routes = [
{ path: 'events', loadComponent: () => // lazy: loaded on demand
import('./events/events.component').then(m => m.EventsComponent) },
{ path: 'profile', canActivate: [authGuard], // blocked unless logged in
loadComponent: () =>
import('./profile/profile.component').then(m => m.ProfileComponent) },
];Theory
Guards, resolvers, and why they help
CanActivate runs before a route loads and returns true or false: FestConnect can block /profile unless the user is logged in. CanDeactivate runs when the user tries to leave: if they have unsaved changes in the registration form, it can warn them first. Resolvers fetch a route's data in advance, so the events page opens with its list already loaded, no flash of empty screen.
Together they make navigation feel deliberate and safe: the right people reach the right pages, at the right time, with data ready.
Practical
BehaviorSubject for shared state (the current user)
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AuthService {
// BehaviorSubject holds a CURRENT value and emits it to new subscribers
private userSubject = new BehaviorSubject<string | null>(null);
user$ = this.userSubject.asObservable();
login(name: string) { this.userSubject.next(name); } // push a new value
logout() { this.userSubject.next(null); }
}
// Any component can subscribe to user$ and always get the latest logged-in user.Quiz
You want to prevent users from opening the /profile page unless they are logged in. Which routing tool fits?
- A resolver, because it fetches data
- A CanActivate guard, which decides whether a route may be entered
- Lazy loading, because it hides the page
- A CanDeactivate guard, because it controls leaving
Show the answer
A CanActivate guard, which decides whether a route may be entered
A CanActivate guard runs before a route is entered and returns true or false, so it is exactly what blocks /profile unless the user is logged in. Option A, a resolver, pre-fetches DATA for a route; it does not decide whether entry is allowed. Option C, lazy loading, controls WHEN a route's code is loaded (on demand) for performance; it does not enforce access rules. Option D, CanDeactivate, controls LEAVING a route (like warning about unsaved changes), which is the opposite direction from what we need here. Entering a route is guarded by CanActivate; leaving it by CanDeactivate.
Think first
Why does a BehaviorSubject suit shared app state better than a plain Subject?
Both a Subject and a BehaviorSubject broadcast values. Why prefer BehaviorSubject for state like the current user? Then tap.
Show the answer
Because a BehaviorSubject remembers its CURRENT value and immediately gives it to any component that subscribes later, whereas a plain Subject only emits to those already listening at the moment a value is pushed. Consider the logged-in user in FestConnect. Suppose the user logs in early, and then a component elsewhere (a navbar, a profile widget) subscribes AFTERWARD to find out who is logged in. With a plain Subject, that late subscriber would hear nothing until the NEXT time a value happens to be pushed, so it would not know the user until they log in or out again, leaving the UI out of sync. A BehaviorSubject, by contrast, is created with (or holds) a current value and replays that latest value to every new subscriber the instant they subscribe, so the navbar immediately learns the user is already logged in. That 'always has a current value to hand out' behaviour is exactly what shared STATE needs: state is a thing that has a value at all times, and any component asking should get the present value, not just future changes. A plain Subject is better for one-off EVENTS (a button click, a notification) where there is no meaningful 'current value' to replay. So the rule of thumb is: BehaviorSubject for state (current value matters), Subject for events (only the moment matters). Current-value replay is what makes BehaviorSubject the right tool for app state.
Summary
Key takeaways
- Lazy loading loads a route's code only when the user navigates there, shrinking the initial bundle.
- CanActivate guards decide whether a route may be entered (e.g. must be logged in); Angular 17 uses functional guards.
- CanDeactivate guards decide whether the user may leave (e.g. warn about unsaved changes).
- Resolvers fetch a route's data before it activates, so the page opens with data ready.
- RxJS Subjects share changing state across components; a BehaviorSubject also holds a current value.
- BehaviorSubject suits app state (like the current user) because it replays the latest value to new subscribers.
- Memory hook: lazy-load for speed, guards for access, resolvers for data, BehaviorSubject for shared state.