🏰 The Grand Hotel of Angular Routing
Imagine you’re building the world’s most amazing hotel. Not just any hotel—one with secret passages, express elevators, VIP sections, and a magical concierge who knows exactly where every guest is at all times!
🎯 What You’ll Master Today
Today we’re exploring Advanced Routing in Angular—the secret blueprints that make your app feel like a well-organized, lightning-fast, super-smart building!
🏠 Child Routes: Rooms Inside Rooms
The Story
Imagine your hotel has a Spa Wing. Inside the Spa Wing, there’s:
- A Massage Room
- A Sauna
- A Pool
You don’t want guests walking through the whole hotel to find these! They enter the Spa Wing first, then choose their room inside.
Child routes work exactly like this!
graph TD A["🏨 Hotel App"] --> B["🧖 Spa Section"] B --> C["💆 Massage"] B --> D["🧖♂️ Sauna"] B --> E["🏊 Pool"]
How It Works
const routes: Routes = [
{
path: 'spa',
component: SpaComponent,
children: [
{ path: 'massage', component: MassageComponent },
{ path: 'sauna', component: SaunaComponent },
{ path: 'pool', component: PoolComponent }
]
}
];
The Magic Part
In your SpaComponent template, you need a special door for child rooms:
<h1>Welcome to the Spa! 🧖</h1>
<nav>
<a routerLink="massage">Massage</a>
<a routerLink="sauna">Sauna</a>
<a routerLink="pool">Pool</a>
</nav>
<!-- Child rooms appear here! -->
<router-outlet></router-outlet>
Real URLs Look Like This
/spa→ Shows Spa welcome/spa/massage→ Shows Spa + Massage room/spa/pool→ Shows Spa + Pool area
💡 Key Insight: The parent stays visible while children swap in and out!
⚡ Lazy Loading: Don’t Carry What You Don’t Need
The Story
Imagine you’re packing for a trip. Would you carry:
- ❌ Every piece of clothing you own?
- ✅ Only what you need for this trip?
Lazy loading means: “Don’t download code until someone actually needs it!”
Why This Matters
| Without Lazy Loading | With Lazy Loading |
|---|---|
| 📦 Download EVERYTHING at start | 📦 Download only home page |
| ⏳ Slow first load | ⚡ Fast first load |
| 😤 User waits | 😊 User happy |
How To Do It
Step 1: Create a feature module with its own routes
// admin.module.ts
@NgModule({
declarations: [AdminDashComponent],
imports: [
RouterModule.forChild([
{ path: '', component: AdminDashComponent }
])
]
})
export class AdminModule { }
Step 2: Load it lazily in your main routes
// app-routing.module.ts
const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.module')
.then(m => m.AdminModule)
}
];
The Magic
When a user visits your app:
- 🏠 Home page loads → AdminModule NOT downloaded
- User clicks “Admin” link → AdminModule downloads NOW
- Future Admin visits → Already cached, instant!
graph LR A["App Loads"] --> B["Home Ready!"] B --> C{User clicks Admin?} C -->|Yes| D["Download Admin"] C -->|No| E["Nothing happens"] D --> F["Admin Ready!"]
🚀 Route Preloading: The Smart Waiter
The Story
A smart waiter at a restaurant doesn’t wait for you to order dessert. While you’re eating your main course, they quietly prepare the dessert menu!
Preloading = Download lazy modules in the background after the main app loads.
Built-in Strategies
Strategy 1: Preload Everything
import { PreloadAllModules } from '@angular/router';
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
]
})
export class AppRoutingModule { }
After home loads → Angular quietly downloads ALL lazy modules!
Strategy 2: Custom Preloading
Want to preload only important routes? Create your own strategy!
@Injectable({ providedIn: 'root' })
export class CustomPreloader
implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>) {
// Check if route has 'preload: true' in data
return route.data?.['preload']
? load()
: of(null);
}
}
Mark routes to preload:
{
path: 'dashboard',
loadChildren: () => import('./dashboard/...'),
data: { preload: true } // ← This one preloads!
},
{
path: 'settings',
loadChildren: () => import('./settings/...'),
// No preload flag = loads only when needed
}
📡 Router Events: The Hotel Announcer
The Story
Imagine a voice over the hotel speakers:
- “Guest is walking to Room 302…”
- “Guest has arrived at Room 302!”
- “Oops! Room 404 doesn’t exist!”
Router Events are Angular’s way of announcing what’s happening during navigation!
The Main Events
graph TD A["NavigationStart"] --> B["RouteConfigLoadStart"] B --> C["RouteConfigLoadEnd"] C --> D["RoutesRecognized"] D --> E["GuardsCheckStart"] E --> F["GuardsCheckEnd"] F --> G["ResolveStart"] G --> H["ResolveEnd"] H --> I["NavigationEnd"] A --> J["NavigationCancel"] A --> K["NavigationError"]
Listening to Events
@Component({...})
export class AppComponent {
constructor(private router: Router) {
this.router.events.subscribe(event => {
if (event instanceof NavigationStart) {
console.log('🚶 Starting navigation to:',
event.url);
this.showLoader = true;
}
if (event instanceof NavigationEnd) {
console.log('🏁 Arrived at:', event.url);
this.showLoader = false;
}
if (event instanceof NavigationError) {
console.log('💥 Navigation failed!',
event.error);
}
});
}
}
Common Uses
| Event | Use Case |
|---|---|
| NavigationStart | Show loading spinner |
| NavigationEnd | Hide spinner, track analytics |
| NavigationError | Show error page |
| RouteConfigLoadStart | “Loading module…” message |
📦 Route Data: Sticky Notes on Doors
The Story
Imagine each hotel room has a sticky note on the door:
- Room 101: “VIP Only! 👑”
- Room 202: “Title: Conference Room”
- Room 303: “Needs breakfast service”
Route Data = Information you attach to routes that components can read!
Static Data
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
data: {
title: 'My Dashboard',
icon: '📊',
requiresAuth: true
}
}
];
Reading Data in Components
@Component({...})
export class DashboardComponent implements OnInit {
title = '';
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Snapshot (one-time read)
this.title = this.route.snapshot.data['title'];
// Or Observable (updates when data changes)
this.route.data.subscribe(data => {
this.title = data['title'];
document.title = data['title'];
});
}
}
Dynamic Data with Resolvers
Need to fetch data BEFORE the page loads? Use a Resolver!
@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<User> {
constructor(private userService: UserService) {}
resolve(route: ActivatedRouteSnapshot) {
const id = route.params['id'];
return this.userService.getUser(id);
}
}
// In routes:
{
path: 'user/:id',
component: UserComponent,
resolve: { user: UserResolver }
}
Now route.data['user'] contains the fetched user!
🗺️ Router State: The Hotel Map
The Story
The concierge always knows:
- Where is every guest right now?
- How did they get there?
- What parameters did they use?
Router State = Angular’s complete knowledge of the current navigation!
Accessing Router State
@Component({...})
export class AnyComponent {
constructor(
private router: Router,
private route: ActivatedRoute
) {}
checkState() {
// Current URL
console.log(this.router.url);
// Output: "/spa/massage?time=60"
// Route parameters
const roomId = this.route.snapshot.params['id'];
// Query parameters
const time = this.route.snapshot
.queryParams['time'];
// Full router state tree
const state: RouterState = this.router.routerState;
console.log(state.snapshot.root);
}
}
The State Tree
graph TD A["RouterState"] --> B["root: ActivatedRoute"] B --> C["firstChild: spa"] C --> D["firstChild: massage"] D --> E["params: id=5"] D --> F["queryParams: time=60"]
Navigating with State
// Navigate with query params
this.router.navigate(['/spa/massage'], {
queryParams: { time: 60, therapist: 'Anna' }
});
// Navigate with state (hidden from URL!)
this.router.navigate(['/checkout'], {
state: { cart: this.cartItems }
});
// Read hidden state
const cart = history.state.cart;
// Or using getCurrentNavigation()
const nav = this.router.getCurrentNavigation();
const cart = nav?.extras?.state?.['cart'];
🎁 Putting It All Together
Here’s our Grand Hotel in code:
const routes: Routes = [
// Lazy-loaded Spa with children
{
path: 'spa',
loadChildren: () =>
import('./spa/spa.module')
.then(m => m.SpaModule),
data: { preload: true, title: 'Spa' }
},
// Lazy-loaded Admin (not preloaded)
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.module')
.then(m => m.AdminModule),
data: { requiresAuth: true }
}
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: CustomPreloader
})
]
})
export class AppRoutingModule { }
🌟 You Did It!
You now understand:
| Concept | What It Does |
|---|---|
| Child Routes | Nested pages within pages |
| Lazy Loading | Download code only when needed |
| Preloading | Smart background downloading |
| Router Events | Listen to navigation announcements |
| Route Data | Attach info to routes |
| Router State | Know where you are & how you got there |
You’re no longer just building apps—you’re architecting experiences! 🏆
Remember: Great routing is invisible. Users don’t notice it—they just feel your app is fast, organized, and delightful!
