Advanced Routing

Back

Loading concept...

🏰 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:

  1. 🏠 Home page loads → AdminModule NOT downloaded
  2. User clicks “Admin” link → AdminModule downloads NOW
  3. 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!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.