Angular Routing
Question 1: What is Angular Routing and how is it implemented?
Answer: Angular Routing is a mechanism for navigating between different components/views in an Angular application. It enables:
- Navigation between pages
- Passing parameters
- Guards for route protection
- Lazy loading of modules
- Child routes and route nesting
Here’s a modern implementation example:
// app.routes.ts
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
component: HomeComponent,
title: 'Home' // New in Angular 15+
},
{
path: 'products',
loadChildren: () => import('./products/products.routes')
.then(m => m.PRODUCT_ROUTES),
canActivate: [authGuard]
},
{
path: 'profile/:id',
component: ProfileComponent,
resolve: {
user: userResolver
}
},
{
path: '**',
component: NotFoundComponent
}
];
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
withComponentInputBinding() // Enable route parameters as inputs
)
]
};
Question 2: How do you implement route guards and resolvers?
Answer: Here’s how to implement route protection and data resolution:
// auth.guard.ts
import { inject } from '@angular/core';
import { Router } from '@angular/router';
export const authGuard = () => {
const router = inject(Router);
const authService = inject(AuthService);
if (authService.isAuthenticated()) {
return true;
}
return router.createUrlTree(['/login']);
};
// user.resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
export const userResolver: ResolveFn<User> = (route) => {
const userService = inject(UserService);
return userService.getUser(route.params['id']);
};
// profile.component.ts
@Component({
selector: 'app-profile',
template: `
<h1>Profile of {{ user().name }}</h1>
<div>Email: {{ user().email }}</div>
`
})
export class ProfileComponent {
id = input.required<number>(); // Route resolved data as input
}
Question 3: How do you implement lazy loading and preloading strategies?
Answer: Here’s how to implement lazy loading and custom preloading:
// app.config.ts
import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(
routes,
withPreloading(PreloadAllModules), // Preload all lazy modules
withComponentInputBinding()
)
]
};
// Custom preloading strategy
import { PreloadAllModules, Route } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SelectivePreloadStrategy implements PreloadAllModules {
preload(route: Route, load: () => Observable<any>): Observable<any> {
return route.data?.['preload'] === true ? load() : of(null);
}
}
// Usage in routes
{
path: 'products',
loadChildren: () => import('./products/products.routes'),
data: { preload: true }
}
Question 4: How do you handle route parameters and query parameters?
Answer:
import { Component, inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-product',
template: `
<h1>Product: {{ productId() }}</h1>
<div>Category: {{ category() }}</div>
`
})
export class ProductComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
// Route Parameter
productId = toSignal(this.route.paramMap.pipe(
map(params => params.get('id'))
));
// Query Parameter
category = toSignal(this.route.queryParamMap.pipe(
map(params => params.get('category'))
));
// Navigation methods
navigateToProduct(id: string) {
this.router.navigate(['/products', id], {
queryParams: { category: 'electronics' },
queryParamsHandling: 'merge'
});
}
}
Interview Tips 💡
Route Configuration Best Practices
// Use constants for route paths export const ROUTES = { PRODUCTS: 'products', PRODUCT_DETAIL: (id: string) => `products/${id}` } as const; // Type-safe route parameters interface ProductRouteParams { id: string; } // Type-safe route data interface ProductRouteData { preload: boolean; roles: string[]; }
Navigation Patterns
// Different navigation methods router.navigate(['/products']); router.navigateByUrl('/products'); // Relative navigation router.navigate(['../'], { relativeTo: route }); // With state router.navigate(['/products'], { state: { from: 'home' } });
Route Guards Patterns
// Combining multiple guards export const adminGuard = () => { return combineLatest([ inject(AuthGuard).canActivate(), inject(RoleGuard).canActivate() ]).pipe( map(([isAuth, hasRole]) => isAuth && hasRole) ); };
Error Handling
// Global 404 handling { path: '**', component: NotFoundComponent, data: { title: 'Page Not Found' } } // Navigation error handling router.events.pipe( filter(event => event instanceof NavigationError) ).subscribe((event) => { // Handle navigation errors });
Testing Routes
describe('ProductComponent', () => { it('should handle route parameters', () => { TestBed.configureTestingModule({ imports: [RouterTestingModule] }); const route = TestBed.inject(ActivatedRoute); route.params.next({ id: '123' }); expect(component.productId()).toBe('123'); }); });
Performance Tips
- Use lazy loading for large modules
- Implement preloading strategies wisely
- Cache resolved data when appropriate
- Use route reuse strategy for complex views
Remember: In interviews, focus on:
- Understanding route configuration and navigation
- Knowledge of guards and resolvers
- Experience with lazy loading
- Handling route parameters and data
- Testing routing scenarios
- Performance optimization techniques
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.