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
User navigates to protected route
User clicks a link or enters URL for /favorites
Router calls canActivate()
Angular Router invokes the AuthGuard’s canActivate() method
Guard checks authentication
Guard subscribes to AuthService.isLoggedInObservable() to check auth state
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.
Navigation in Components
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 ([ '' ]);
}
}
Navigation Methods
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' );
Navigate declaratively in templates: < a routerLink = "/favorites" > My Favorites </ a >
< a [routerLink] = "['/auth', 'login']" > Login </ a >
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
Use lazy loading for feature modules
Load features on-demand to reduce initial bundle size
Protect sensitive routes with guards
Use CanActivate guards to enforce authentication requirements
Order routes from specific to general
Place wildcard routes last, and more specific routes before general ones
Use forChild() in feature modules
Only use RouterModule.forRoot() once in AppRoutingModule
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