Skip to main content
ScreenPulse uses Angular Router to manage navigation between features. Routes are configured with lazy loading to optimize performance and guards to protect authenticated routes.

Route Configuration

The main routing configuration defines top-level routes with lazy-loaded feature modules:
src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './core/guards/auth.guard';

const routes: Routes = [
  { 
    path: '', 
    loadChildren: () => import('./pages/search/search.module')
      .then(m => m.SearchModule) 
  },
  
  { 
    path: 'favorites',
    canActivate: [AuthGuard], 
    loadChildren: () => import('./pages/favorites/favorites.module')
      .then(m => m.FavoritesModule) 
  },
  
  { 
    path: 'auth', 
    loadChildren: () => import('./pages/auth/auth.module')
      .then(m => m.AuthModule) 
  },
  
  { 
    path: '**', 
    redirectTo: '' 
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Lazy Loading

Instead of loading all features upfront, ScreenPulse uses the loadChildren property to load feature modules on-demand:
{
  path: 'favorites',
  loadChildren: () => import('./pages/favorites/favorites.module')
    .then(m => m.FavoritesModule)
}
When a user navigates to /favorites, Angular downloads the FavoritesModule bundle and its dependencies, then instantiates the module and displays the appropriate component.

Benefits of Lazy Loading

Faster Initial Load

Users download only the code needed for the initial route, reducing time-to-interactive.

Smaller Bundles

Each feature is bundled separately, allowing browsers to cache and load them independently.

Better Performance

Features are loaded in the background as users navigate, providing a smooth experience.

Scalable

Add new features without impacting the size of existing bundles.

Route Protection with Guards

The AuthGuard protects routes that require authentication, such as the favorites page:
src/app/core/guards/auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { Observable } from 'rxjs';
import { tap, take } from 'rxjs/operators';
import { ToastrService } from 'ngx-toastr';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(
    private authService: AuthService,
    private router: Router,
    private toastService: ToastrService
  ) { }

  canActivate(): Observable<boolean> {
    return this.authService.isLoggedInObservable().pipe(
      take(1),
      tap(loggedIn => {
        if (!loggedIn) {
          this.toastService.warning(
            'You must be logged in to access this page.',
            'Access Denied'
          );
          this.router.navigate(['/auth/login']);
        }
      })
    );
  }
}

How Guards Work

1

User navigates to protected route

User clicks a link or enters URL for /favorites
2

Router calls canActivate()

Angular Router invokes the AuthGuard’s canActivate() method
3

Guard checks authentication

Guard subscribes to AuthService.isLoggedInObservable() to check auth state
4

Route activation or redirect

  • If authenticated: Returns true, route loads
  • If not authenticated: Shows warning toast, redirects to /auth/login
The take(1) operator ensures the subscription completes after checking authentication once, preventing memory leaks.

Feature Module Routing

Each feature module has its own routing configuration:
src/app/pages/search/search-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SearchComponent } from './page/search.component';

const routes: Routes = [
  {
    path: '',
    component: SearchComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class SearchRoutingModule { }
Notice RouterModule.forChild(routes) instead of forRoot(). Use forRoot() only in AppRoutingModule, and forChild() in all feature modules.

Wildcard Route

The wildcard route (**) catches all unmatched URLs and redirects to the home page:
{ 
  path: '**', 
  redirectTo: '' 
}
Always place the wildcard route last in your routes array. Routes are matched in order, so the wildcard would catch everything if placed earlier.
Components use the Router service to navigate programmatically:
src/app/layout/navbar/navbar.component.ts
import { Router } from '@angular/router';
import { AuthService } from 'src/app/core/services/auth.service';

export class NavbarComponent {
  constructor(
    private authService: AuthService,
    private router: Router
  ) { }

  logOut() {
    this.authService.logOut();
    this.router.navigate(['']);
  }
}
Navigate using an array of route segments:
this.router.navigate(['/favorites']);
this.router.navigate(['/auth', 'login']);
Navigate using a full URL string:
this.router.navigateByUrl('/auth/login');

Route Parameters

While ScreenPulse primarily uses simple routes, Angular supports route parameters for dynamic URLs:
// Route configuration
{ path: 'movie/:id', component: MovieDetailComponent }

// Component
export class MovieDetailComponent implements OnInit {
  constructor(private route: ActivatedRoute) { }
  
  ngOnInit() {
    const id = this.route.snapshot.paramMap.get('id');
    // Or use observable for parameter changes:
    this.route.paramMap.subscribe(params => {
      const id = params.get('id');
    });
  }
}

Routing Best Practices

1

Use lazy loading for feature modules

Load features on-demand to reduce initial bundle size
2

Protect sensitive routes with guards

Use CanActivate guards to enforce authentication requirements
3

Order routes from specific to general

Place wildcard routes last, and more specific routes before general ones
4

Use forChild() in feature modules

Only use RouterModule.forRoot() once in AppRoutingModule
5

Unsubscribe from route observables

Use take(1) or store subscriptions to prevent memory leaks

Route Structure

/                    → SearchModule (lazy-loaded)
/favorites           → FavoritesModule (lazy-loaded, AuthGuard protected)
/auth/login          → AuthModule → LoginComponent (lazy-loaded)
/auth/register       → AuthModule → RegisterComponent (lazy-loaded)
/**                  → Redirect to /

Next Steps

Lazy Loading

Deep dive into lazy loading and code splitting

Services

Learn about AuthService and route guards

Module Structure

Understand feature module organization

RxJS Patterns

Explore reactive routing with observables

Build docs developers (and LLMs) love