Routing & Navigation in Angular
Angular Routing & Navigation
Table of contants
- Router Basics
- Route Parameters
- Query Parameters
- Child Routes
- Lazy Loading
- Route Guards
- Preloading Strategies
- Interview Questions
Router Basics
What is Angular Router?
Angular Router is a powerful navigation library that enables navigation from one view to another based on URL changes. It interprets browser URLs as instructions to navigate to a client-generated view.
Setting up Router
app.config.ts (standalone)
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
export const routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'about', component: AboutComponent},
{path: '**', component: PageNotFoundComponent} // wildcard route
];
app.component.html
<nav>
<a routerLink="/home" routerLinkActive="active">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
</nav>
<router-outlet></router-outlet>
Router Method
import { Router } from '@angular\router';
export class AppComponent{
constructor(private router: Router){}
navigateTAbout(){
// Programmatic navigation
this.router.navigate(['/about']);
// Navigate with extras
this.router.navigate(['/about'], {
queryParams: {id: 123},
fragment: 'section1'
});
}
navigateRelative(){
//Relative navigation
this.router.navigation(['../sibling'], { relativeTo: this.route });
}
}
Route Parameters
Required route paramenters
Route paramenters are part of the URL path and are mandatory for the route to match.
Defining route with parameters
export const routes: Routes =[
{path: 'user/:id', component:UserDetailsComponent},
{path: 'product/:category/:id', component: ProductComponent}
];
Accessing Route paramemters - snapshot
import { Component, OnInit} from '@angular\core';
import { ActivatedRoute } from '@angular\router';
@component({
selector: 'app-user-detail',
template:`<h2>User ID: {{userId}} </h2>`
})
export class UserDetailsComponents implements OnInit {
userId: string = '';
constructor(privete route:ActivatedRoute){}
ngOnInit(){
//using snapshot (for one time read)
this.userId = this.route.snapshot.paramMap.get('id') ||'';
}
}
Accessing Route Parameters -Observable
import { Component, OnInit} from '@angular\core';
import { ActivatedRoute } from '@angular\router';
@component({
selector: 'app-user-detail',
template:`<h2>User ID: {{userId}} </h2>`
})
export class UserDetailsComponents implements OnInit {
userId: string = '';
constructor(privete route:ActivatedRoute){}
ngOnInit(){
//using Observable (for reactive updates)
his.route.paramMap.subscribe(params =>{
this.userId = params.get('id')||'';
// Load user data based on new Id
});
}
}
Navigating with Parameters
// Template
<a [routerLink]="['/user', userId]">View User </a>
//Component
this.router.navigate(['/user', userId]);
this.router.navigate(['/product', category, productId]);
Query Paramenters
Query parameters are optional parameters that apear after the ? in the URL.
Using Query Parameter
Defining Query Parameters
// Template
<a [routerLink]="['/products']"
[queryParams]="{category: 'electronics', sort: 'price' }">
Products
</a>
// Component
this.router.navigate(['/products'], {
queryParams: {
category: 'electronics',
sort: 'price',
page: 1
}
});
Accessing Query Parameters
import {Component, OnInit} from '@angualr/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-products',
templateUrl: './products.component.html'
})
export class ProductComponent implements OnIntit {
category: string = '';
sortBy: string = '';
page: number =1;
constructor(private router: ActivatedRoute){}
ngOnInit(){
//Using Snapshot
this.category = this.router.snapshot.queryParamMap.get('category') || '';
// Using observable
this.route.queryParamMap.subscribe(params =>{
this.category = params.get('cotegory') || '';
this.sortBy = params.get('sort') || 'name';
this.page = Number(params.get('page')) ||1;
this.loadProducts();
})
}
loadProducts(){
// load Product based on query params
}
}
Query Params Handling Opetions
//Merge with existing query params
this.router.navigate(['/products'], {
queryParams: {page: 2},
queryParamsHandeling: 'Image' // Keeps exiting params
});
//Preserve all existing query params
this.router.nevigate(['/other-page'], {
queryParamsHandling: 'preserve'
});
Child Routes
Child routes allow you to create nested routing structures for component hirerachies.
Defining Child Routes
app.route.ts
import { Routes } from '@angualr/router';
export const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
children: [
{path: '', redirectTo: 'dashboard', pathMatch: 'full'},
{path: 'dashboard', component: DashboardComponent},
{path: 'User', component: UserComponent},
{path: 'setting', component: SettnigComponent},
{
path: 'reports',
component: ReportComponent,
children: [
{path: 'sales', component: SalesComponent},
{path: 'inventory', component: InvonteryComponent}
]
}
]
}
]
admin.component.html (Parent Component)
<div class="admin-container">
<nav class="sidebar">
<a routerLink="dashboard" routerLinkActivated="Active"> Dashboard </a>
<a routerLink="users" routerLinkActivated="Active"> Users </a>
<a routerLink="settings" routerLinkActivated="Active"> Settings</a>
<a routerLink="reports" routerLinkActivated="Active"> Reports </a>
</nav>
<div class="content">
<!-- Child component render here -->
<router-outlet></router-outlet>
</div>
</div>
Accessing Parant Route Data
import {Component, OnInit} from '@angualr/core';
import {ActivatedRoute} from '@angular/router';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html'
})
export class DashboardComponent implements OnInit{
constructor(Private route: ActivatedRoute){}
ngOnInit(){
// Access parent route data
const parentData = this.parent?.snapshot.data;
// Acces parent route params
const parentData = this.parent?.snapshot.paramMap;
}
}
Lazy Loading
Lazy loading helps reduce initial bundle size by loading feature modules on demand.
Implementing Lazy Loading
app.routes.ts
import { Routes } from '@angular/router';
export class routes: Routes = [
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
// Lazy load modules
{
path: 'products',
loadChildren: ()=> import('./products/products.routes')
.then(m => m.PRODUCTS_ROUTES)
},
{
path: 'admin',
loadChildren: ()=> import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES),
canActivate: [AuthGuard] // Guard applied to lazy loded module
}
];
products/products.routes.ts
import {Routes } from '@angular/router';
import { ProductListComponent} from './product-list/product-list.component';
import { ProductDetailComponent} from './product-Detail/product-Detail.component';
export const PRODUCTS_ROUTES: Routes = [
{path: '', component: ProductListComponent},
{path: ':id', component: ProductDetailComponent}
];
Lazy Loading a component (Angular17+)
export const routes: Routes =[
{
path: 'about',
loadComponent: () => import('about/about.component')
.then(m => m.AboutComponent)
}
];
Benifits of Lazy Loading
- Reduced initial bundle size
- Faster initial load time
- Load features only when needed
- Batter performance for larze application
Route Guards
Route guards control navigation to and from routes. They are implemented as functions (functional guard) or classes (Angular < v15>).
Type of Route Guards
- CanActivate - Controls route activation
- CanActivateChild - Cntrols child route activation
- CanDeactivate - Controls route deactivation
- CanLoad - Controls Lazy loading of modules (deprecated in favor of canMatch)
- CanMatch - Controls if a route can be matched
- Resolve - pre-fatches data before route activation
AuthGuard (CanActivate) - Functional Approch
auth.guard.ts
import { inject } from '@angular\core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivationFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if(AuthService.isAuthenticated()){
return true;
}
// Store the attempted URL for redirecting after login
authService.redirectUrl = state.url;
// Navigate to login page
router.navigate(['/login']);
return false
};
Using AuthGuard
export const routes: Routes =[
{path: 'login', component: LoginComponent},
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard]
},
{
path: 'admin',
component: AdminComponent,
canActivate: [authGuard, roleGuard], //Multiple Guards
data: {roles: ['admin']}
}
];
Role-Based Guard
role.guard.ts
import { inject } from '@angular\core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const roleGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
const requiredRoles = route.data['roles'] as string[];
const userRole = authService.getUserrole();
if(requiredRoles.include(userRole)){
return true;
}
// Redirect to unathorized page
router.navigate(['/unauthorized']);
return false;
};
CanActivateChild Guard
admin.guard.ts
import { inject } from '@angular\core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const adminGuard: CanActivateChildFn = (route, state) => {
const authService = inject(AuthService);
// Check if user has admin privileges
return authService.hasAdminAccess();
};
Using CanactivateChild
export const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivateChild: [adminGuard],
children: [
{path: 'users', component: UsersComponent},
{path: 'settings', component: SettingsComponent}
]
}
];
CanDeactivate Guard
unsaved-changes.guard.ts
import { canDeactivateFn } from '@angular/router';
export interface CanComponentDeactivate {
canDeactivate: () => boolean || Promise<boolean>;
}
export const unsavedChangesGuard: CanDeactivateFn<CanComponentDeactivate> =
(comonent) => {
if(component.canDeactivate){
return component.canDeactivate();
}
return true;
};
form.component.ts
import { Component } from '@angular/core';
import {CanComponentDeactivate } from './unsaved-changes.guard';
@Component({
selector: 'app-form',
templateUrl: './form.component.html'
})
export class FormComponent implements CanComposeDeactivate {
formChanged: boolean = false;
canDeactivate(): boolean {
if(this.formChanged){
return confirm('You have unsave changes. Do you realy want to leave?');
}
return true;
}
OnFormChange(){
this.formChanged = true;
}
}
Using CanDeactivate
export const routes: Routes = [
{
path: 'edit/:id',
component: FromComponent,
canDeactivate: [unsavedChangesGuard]
}
];
Async Guard with Observable
auth.guard.ts (Observable-based)
import { inject } from '@angular\core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
import { map } from 'rxjs/operators';
export const authGuard: Canactivate = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
return authService.isLoggedIn$.pipe(
map(isLoggedIn => {
if(isLoggedIn) => {
return true;
}
router.navigate(['/login']);
return false;
})
);
};
Class-Based Guards ( Legacy - Angular < v15)
auth.guard.ts(class-based)
import { injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanActivate,
Router
} from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
provideIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
){}
canActivate(
route: ActivateRouteSnapshot,
state: RouterStateSnapshot
): boolean {
if(this.authService.isAuthenticated()){
return true;
}
this.router.navigate(['/login'],{
queryParams: {returnUrl: state.url}
});
return false;
}
}
Preloading Strategies
Preloading strategies determaine when and how lazy-loaded modules are loaded in the background.
Built-in Preloading Strategies
1. NoPreloading (Default)
import { ApplicationConfig } from '@angualr/core';
import { provideRouter, NoPreloading } from '@angualr/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
Providers: [
provideRouter(
routes,
withPreloading(NoPreloading) // No preloading
)
]
};
2. PreloadAllModules
import { ApplicationConfig } from '@angualr/core';
import { provideRouter, PreloadAllModules } from '@angualr/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
Providers: [
provideRouter(
routes,
withPreloading(PreloadAllModules) // Preload all lazy modules
)
]
};
Custom Preloading Strategy
selective-preloading-strategy.ts
import { Injectable } from '@angualr/core';
import { Route, PreloadingStrategy } from '@angualr/router';
import { of, Observable } from 'rxjs';
@Injectable({
provideIn: 'root'
})
export class SelectivePreloadingStrategy implements PreloadingStrategy{
preloadedModules: string[] = [];
preload( route: Route, load: () => Observable<any>): Observable<any>{
if(route.data && route.data['preload']){
// add the route path to the preloaded module array
this.preloadedModules.push(route.path || '');
console.log('Preload: ' + route.path);
return load();
}else{
return of(null);
}
}
}
Using Custom Preloading Strategy
import { ApplicationConfig } from '@angualr/core';
import { provideRouter } from '@angualr/router';
import { routes } from './app.routes';
import { SelectivePreloadingStrategy } from './Selective-Preloading-Strategy';
export const appConfig: ApplicationConfig = {
Providers: [
provideRouter(
routes,
withPreloading(SelectivePreloadingStrategy)
)
]
};
app.route.ts with preload data
export const routes: Routes = [
{
path: 'products',
loadChildren: () => import('./products/product.routes')
.then(m => m.PRODUCT_ROUTES),
Data: { preload: true }// This module will be preloaded
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES),
Data: { preload: false }// This module will NOT be preloaded
}
];
Network-Aware Preload Strategy
network-aware-preload-strategy.ts
import { Injectable } from '@angualr/core';
import { Route, PreloadingStrategy } from '@angualr/router';
import { of, Observable } from 'rxjs';
@Injectable({
provideIn: 'root'
})
export class NetworkAwarePreloadStrategy implements PreloadingStrategy{
preload(route: Route, load: ()=> Observable<any>): Observable<any>{
// Check if user is on a slow connection
const connection = (navigator as any).connection;
if(connection){
// Don't preload on slow connections or save-data mode
if(connection.saveData || connection.effectiveType ==='slow-2g' ||
connection.effectiveType === '2g'
){
return of(null);
}
}
// Preload if data flag is set
if(route.data && route.data['preload']){
return laod();
}
return of(null);
}
}
InterView Questions
Basic Lavel (1-2 years exp)
Q1. What is Angular routing and why do we need it?
Answer: Angular Router is a navigation library that enables navigation between views/components bassed on URL changes. We needit because:
- Enables Single-page application (SPA) navigation
- Maps URLs to components
- Maintains browser history
- Supports deep linking
- Provides programitic navigation
- Hadles route parameter and query string
Q2. What is difference between routerLink and href?
Answer:
- routerLink: Angular directive that prevents full page reload, updates URL via History API, maintains application state, and is SPA-frindely -href: Standard HTML attribute that causes full page reload, loses application state, and makes server request
<!-- Good for SPA -->
<a routerLink="/about">About</a>
<!-- Causes page reload -->
<a href="/about">About</a>
Q3. What is <router-outlet> and what it its purpose?
Angular: <router-outlet> is a directive that acts as a placholder where the Angular Router inserts the component for the current route. It makes the spot in the tamplate where the routed component should be displayed.
<nav>...</nav>
<router-outlet></router-outlet> <!--Component load here-->
<footer>...</footer>
Q4. How do you navigate programmatically in Angualr?
Answer:
import { Router } from '@angular/router';
constructor(private router: Router){}
// Simple navigation
this.router.navigate(['/products']);
// with paramenter
this.router.navigate(['/products', productId]);
// with query params
this.router.navigate(['/products'], {
queryParams: {category: 'electronics'}
});
// Using navigationByUrl
this,router.navigateByUrl('/about');
Q5: What is difference between navigate() and navigateByUrl()?
Answer:
navigate()- Takes an array of URL segments, supports relative navigation, and can use route parametersnavigateByUrl()- Takes a complate URL string, always absolute navigation, trates Url as a single string
// navigate - array for segments
this.router.navigate(['/user', userId, 'profile']);
// navigationByUrl - a complete URL string
this,router.navigateByUrl('/user/123/profile');
Q6: What are route paramenters and how do you access them?
Answer: Route paramerer are dynamic sengment in URL path
// Define route
{path: 'user\:id', component: UserComponent}
// Access in component
export class UserComponent implements Oninit {
constructor(private router: ActivatedRoute){}
ngOninit(){
//snapshot (one-time read)
const id = this.route.snapshot.paramMap.get('id');
// Observable (reactive)
this.router.paramMap.subscribe(params => {
const id = params.get('id');
});
}
}
Q7. What is differece between route parameters query paramaters?
Answer: -Route Paramenters: Part of the route path, required for route matching, used for essential data - Example: /user/123 where 123 is a route parameter -Query Parameter: Optional, appear after ?, used for filters/sorting/pagination - Example: /products?category=electronics&short=price
Q8. What is routerLinkActive directive?
Answer: routerLinkActive adds CSS classes to an element when its associated routerLink is active.
<a routerLink="/home" routerLinkActive="active">Home </a>
<a routerLink="/about" routerLinkActive="active"
[routerLinkActiveOptions]="{exact: true}">About </a>
.active{
font-weight: bold;
color: blue;
}
Intermediate Level (2-3 Years Expericnce)
Q9: Explain child route in Angula. How do you implement them?
Anwer: Child routes create nested routing structures for component hierarchies.
const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
children: [
{path: 'dashboard', component: DashboardComponent },
{path: 'users', component: UsersComponent }
]
}
];
Parent component needs a <router-outlet> for child components. URLs become /admin/dashboard and /admin/users.
Q10: What is lazy loading in Angular routing? What are its benefits?
Angular: Lazy loading loads features modules on-demand rather than at application startup.
Benefits:
- Reduced initial bundle size
- Faster initial load time
- Better performance
- Load features only when needed
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
Q11: What are the Route Guard? List all types of route guards.
Answer:
- CanActivate - Can route be activated?
- CanActivateChild - Can child routes be activated?
- CanDeactivate -- Can user leave the route?
- CanMatch - Can route be matched?
- Resolve - Pre-fatch data before activation
Q12: Explain CanActivate guard with an example.
Answer: CanActivate determines if a route can be activated.
import { inject } from '@angular\core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivationFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if(AuthService.isAuthenticated()){
return true;
}
// Navigate to login page
router.navigate(['/login']);
return false
};
//Usage
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard]
}
Q13. what is CanDeactivate guard and when would you use it?
Answer: CanDeactivate prevents navigation away from a route, typically used to warn users about unsaved changes.
export const unsavedChangesGuard: CanDeactivateFn<FromComonent> =
(component) => {
if(component.hasUnsavedChanges()){
return confirm('You have unsaved changes. Leave anyway');
}
return true;
};
Use cases:
- Form with unsaved changes
- Ongoing file upload
- Incomplate wizard steps
Q14: What is difference between CanActivate and CanActivateChild?
Answer:
- CanActivate: Guard the route itself
- CanActivateChild: Guard all child routes of a parent route
{
path: 'admin',
component: AdminComponent,
canActivate: [authGuard], //Guards /admin
canActivateChild: [adminGuard], //Guards /admin/users, /admin/settings
children: [
{path: 'users', component: UsersComponent },
{path: 'settings', component: settingsComponent }
]
}
Q15. How do you pass data to a route?
Answer: Multiple ways:
1. Route Parameters:
this.router.navigate(['/user', 123]);
2. Query Paramenters:
this.router.navigate(['/products', {
queryParams: {category: 'electronics'}
}]);
3. static data:
{
path: 'about',
component: AboutComponent,
data: {title: 'About Us', breadcrumb: 'About'}
}
// Access
this.router.data.subscribe(data=> {
console.log(data['title']);
});
4. State:
this.router.nivigate(['/result'], {
state: {searchResults: results}
});
// Access
const state = history.state;
console.log(state.searchResults);
Q16. What is preloading strategies? Name the built-in strategies.
Answer: Preloading strategies determine when lazy-loaded modules are loaded.
Built-in strategies:
- NoPreloading (default) - don't preload any modules
- PreloadAllMudeles - Preload all lazy modules after initial load
proviedRouter(
routes,
withPreloading(PreloadAllMudeles)
)
Q17: How do you create a custom preloading strategy?
Answer:
import { Injectavble } from '@angular/core';
import { PreloadingStrategy, Route} from '@angular/router';
import {Observable, of} from 'rxjs';
@Injectable({ providedIn: 'root'})
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: ()=> Observable<any>): Observable<any> {
if(route.data && route.data['preload']) {
console.log('Preloading: ' + route.path);
return load();
}
return of(null);
}
}
// route configuration
{
path: 'products',
loadChilderen: () => import('./products/products.routes'),
data: {preload: true}
}
// App config
provideRouter(routes, withPreloading(SelectivePreloadingStrategy))
Q18. What is the wildcard route and where should it be placed?
Answer: Wildcard route (**) matches any URL not matched by previous routes. Must be placed last in route configuration.
const routes: Routes =[
{path: '', component: HomeComponent},
{path: 'about', component: aboutComponent},
{path: '**', component: PageNotFoundComponent} // Must be in last
];
Q19. How do you handle query parameter in Angular?
Answer:
//set query paramenter
this.router.navigate(['/products'], {
queryParams: {category: 'electronics', sort: 'price'},
queryParamsHandeling: 'merge' // merge, preserve, or empty
});
//Read query params
this.route.queryParaMap.subscribe(params => {
const category = params.get('category');
const sort = params.get('sort');
})
// Snapshot (one-time read)
const category = this.route.snapshot.queryParamsMap.get('category');
Q20. What is difference between paramMap and params in ActivatedRoute?
Answer:
- paramMap: Return ParamMap object with helper methods (
get(),getAll(),has(),keys) - params: Return plain JavaScript object
//paramMap (recommended)
this.route.paramMap.subscribe(params => {
const id = params.get('id'); // type-safe
const hasId = params.has('id');
});
//params (lagacy)
this.route.params.subscribe(params =>{
const id = params['id']; // direct access
});
Advance Level (3+ year Exp)
Q21: How would you implement role-based authentication with route guards?
Answer:
export const roleGuard: canActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
const requiredRoles = route.data['roles'] as string[];
const userRole = authService.getUserRole();
if(!authService.isAuthentcated()){
router.navigate(['/login']);
return false;
}
if(requiredRole.includes(userRole)){
return true;
}
router.navigate(['/unauthorized']);
return false;
};
// Route configuration
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard, roleGuard],
data: {roles: ['admin', 'superAdmin']}
}
Q22: How do you handle authentication tokens and refresh tokens with route guards?
Answer:
export const authGuard: CanActivatedFn = async (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if(!authService.hasValidToken()) {
try{
// Try to refresh token
await authService.refreshToken().toPromise();
return true;
} catch (error) {
// refresh failled. redirect to login
authService.logOut();
router.navigate(['/login'], {
queryParams: {returnUrl: state.url }
});
return false;
}
}
};
Q23: What is resolve guard and how is it different from other guards?
Answer: Reslove guard pre-fetches data bedore route activation, ensuring data is available when component initializes.
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { USerService } from './user.service';
export const userResolver: ResolveFn<User> = (route, sate) => {
const userService = inject(USerService);
const userId = route.paramMap.get('id');
return userService.getUser(userId);
};
// Route configuration
{
path: 'user/:id',
component: UserComponent,
resolve: {user: userResolver }
}
//Component
ngOninit(){
this.route.data.subscribe(data => {
this.user = data['user']; // Data already loaded
});
}
Difference from other guards:
- Other guards return boolean (allow/ deny)
- Resolve returns data (Observable/Promise)
- Delays navigation until data is not loaded
Q24. How do you implements multiple guards on a single route? What is a execution order?
Answer:
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [authGuard, roleGuard, subscriptionGuard]
}
Execution order
- Guards execute in the order they're defined (left to right)
- If any guard return false, subsequect guard don't execute
- All guard must reutrn true for navigation to proceed
Q25. How would network -aware preloading strategy?
Answer:
export class NetworkAwarePreloadStrategy implements PreloadingStrategy {
preload(route: Route, load() => Observable<any>): Observable<any> {
const connection = (navigator as any).connection;
if(connection){
//Don't preload on slow connection
if(connection.saveData || connection.effectiveType==='slow-2g' ||connection.effectiveType==='2g' ) {
return of(null);
}
}
// Check battery status
if((navigator as any).getBattery) {
return from((navigator as any).getBattery()).pipe(
margeMap((battery: any) =>{
// Don't preload if battery is low or not charging
if(battery.level<0.2 && !battery.charging){
return of(null);
}
return route.data?.['preload']? load() : of(null);
})
);
}
return route.data?.['preload'] ? load(): of(null);
}
}
Q26: How do you test route guards?
Answer:
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { authGuard } from './auth.guard';
impoort { AuthService } from './auth.service';
describe('authGuard', () =>{
let authService: jasmine.SpyObj<AuthService>;
let router: jasmine.SpyObj<Router>;
beforeEach(() => {
const authSpy = jasmine.createSpyObj('AuthService', ['isAuthenticated']);
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
TestBed.configureTestingModule({
providers: [
{ provide: AuthService, useValue: authSpy },
{provide: Router, useValue: routerSpy}
]
});
authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
router = TestBed.inject(router) as jasmine.SpyObj<Router>;
});
it('sould allow activation when Authenticated', () => {
authServise.isAuthenticated.and.returnValue(true);
const result = authGuard(
{} as any,
{url: '/dashboard'} as any
);
expect(result).toBe(true);
})
it('should redirect to login when not authenticated', () => {
authServise.isAuthenticated.and.returnValue(false);
const result = authGuard(
{} as any,
{url: '/dashboard'} as any
);
expect(result).toBe(false);
expect(router.navigatge).toHaveBeenCalledWith(['/login']);
});
});
Q27: What is difference between snapshot and Observable approch for route parameters?
Answer:
Snapshot:
- One-time read
- Use when component is destroyed and recreated on nevigation
- Simpler, Synchronous access
const id = this.route.snapshot.paramMap.get('id');
Observalbe:
- Reactive updates
- Use when navigation to same component with different parameters
- Required when route reuse stratgy keeps component alive
this.route.paramMap.subscribe(params => {
const id = params.get('id');
this.loadData(id);
});
Example scenario:
//Navigation from /user/1 to /user/2
//Component is reused, snapshot won't update
// Observable will emit new value
Q28: How do you implement auxiliary routes (named outlets)?
Answer: Auxiliary routes allow multiple independent router outlets in the same component.
<!-- app.component.html -->
<router-outlet></router-outlet> <!-- Primary outlet -->
<router-outlet name="sidebar"></router-outlet> <!-- Named outlet -->
<router-outlet name="popup"></router-outlet>
// Route configuration
{
path: 'home',
component: HomeComponent
},
{
path: 'chat',
component: ChatComponent,
outlet: 'sidebar'
},
{
path: 'alert'
component: alertComponent,
outlet: 'popup'
}
// Navigation
this.router.navigate([
{Outlets: {primary: 'home', sidebar: 'chat', popup: 'alert '} }
]);
// URL: /home (sidebar: chat//popup:alert)
// Close named outlet
this.router.navigate([{ outlets: {popup: null}}]);
Q29: How would you implement a breadcrumb navigation system?
Answer:
// Route configuration with breadcrumb data
{
path: 'products',
component: ProductComponent,
data: {breadcrumb: 'Products'},
children: [
{
path: ':id',
component: ProductDetailComponent,
data: {breadcrumb: 'Product Detail'}
}
]
}
// Breadcrumb component
@Component({
selector: 'app-breadcrumb',
template: `
<nav>
<a *ngFor="let breadcrumb of breadcrumbs"
[routerLink]="breadcrumb.url">
{{breadcrumb.label}}
</a>
</nav>
`
})
export class BreadcrumbComponent implements onInit {
breadcrumbs: Breadcrumb[] = [];
constructor(
private router: Router,
private activatedRouter: ActivatedRoute
){}
ngOnInit(){
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
this.breadcrumbs = this.createBreadcrumbs(this.activatedRoute.root);
});
}
private createBreadcrumbs(
route: activatedRoute,
url: string = '',
breadcrumbs: Breadcrumb[] = []
): Breadcrumb[] {
const children: activatedRoute[] = route.children;
if(children.length===0){
return breadcrumbs;
}
for(const child of children){
const routeURL: string = child.snapshot.url
.map(segment => segment.path)
.join('/');
if(routeURL !==''){
url += `/${routeURL}`;
}
const label = child.snapshot.data['breadcrumb'];
if(label){
breadcrumbs.push({label, url});
}
return this.createBreadcrumbs(child, url, breadcrumbs);
}
return breadcrumbs;
}
}
Q30. How do you handle route events and what are the different type?
Angular: Angular Router emits various events during navigation lifecycle.
import {
Router,
NavigationStart,
NavigationEnd,
NavigationCancel,
NavigationError
} from '@angular/router';
export class AppComponent {
loading = false;
constructor (private router: Router){
this.router.events.subscribe(event => {
if(event instanceof NavigatinStart) {
this.loading = true;
console.log('Navigation statted:', event.url);
}
if(event instanceof NavigationEnd){
this.loading = false;
console.log('Navigation Ended: ', event.url);
// Google Analytics tracking
this.trackPageView(event.url);
}
if(event instanceof NavigationCancel){
this.loading = false;
console.log('Navigation cancelled: ', event.url);
}
if(event instanceof NavigationError){
this.loading = false;
console.log('Navigation Error: ', event.error);
}
});
}
trackPageView(url: string){
// Analytics tracking logic
};
}
Router Events:
- NavigationStart - Navigation Begins
- RoutesRecognized - Routes parsed
- RouteConfigLoadStart - Before Lazy loading
- RouteConfigLoadEnd - After lazy loading
- NavigationEnd - Navigation successful
- NavigationCancel - Navigation Cancel (gurad returns false)
- NavigationError - Navigation Error
- Scroll - Scroll position change
Additional Interview Tips
Q31: What are route reuse strategies?
Answer: RouterReuseStrategy cntrols how Angular reuse route components.
import { RouterReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
export class CustomReuseStrategy implements RouterReuseStrategy {
private handlers: { [key: string]: DetachedRouteHandle } = {};
shouldDetach( route: ActivatedRouteSnapshot): boolen {
return route.data['shouldReuse'] || false;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
if(route.data['shouldReuse']) {
this.handlers[route.routeConfig!.path!];
}
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return !!thi.handlers[route.routeConfig!.path!];
}
retrieve(route: ActivatedRouteSnapshot):DetachedRouteHandle {
return this.handlers[route.routerConfig!.path!];
}
shouldReuseRoute(future:ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) :boolean {
return future.routeConfi === curr.routeConfig;
}
}
// Provide in app.config.ts
providers: [
{provide:RouterReuseStrategy, useClass: CustomReuseStrategy}
]
Q32: How do you implement location strategy vs path location strategy?
Answer:
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withHashLocation } from '@angular/core';
// Hash Location Strategy (URLs with #)
// Example: http://example.com/#/products
export const appConfig: ApplicationConfig ={
providers: [
provideRouter(routes, withHasLocation())
]
};
// path loacation Strategy (default - HTML5 pushState)
// Example: http://example.com/products
export const appConfig: ApplicationConfig ={
providers: [
provideRouter(routes) // Default is PAthLocationStrategy
]
};
When to use Hash Location
- Server does not support URL rewriting
- Static file hosting (Guithub Pages)
- Legacy browser support
When to use Path Location
- Modern application
- SEO importent
- Server support URL rewriting
Q33: What is the CanMatch Guard and How it is different from CanActivate?
Answer: CanMatch (Angualr 14+) determines if a route can be matched before attempting to load it.
export const featureGuard: CanMatchFn = (route, segments) => {
const featureService = inject(FeatureService);
return featureService.isFeatureEnabled('product');
}
//Route configuration
{
path: 'products',
loadChildren: () => import('./products/product.routes'),
canMatch: [featureGuard]
}
Differences:
- CanMatch : Evaluated before route matching, prevents lazy loading, can have fallback routes
- CanActivate: Evaluated after route matching, module already loaded, can't have fallback
Use case: Feature flags, A/B testing, conditional routing
Best Practices
- Use functional guards (Angular 15+) instead of class-based guards
- Prefer lazy loading for feature modules to improve performance
- Use router resolvers to pre-fatch data and avoid loading spinners in components
- Implement custom preloading strategies based on user behaviour or network conditions
- Use route data for configuration instead of heardcoding in components
- Always handle errors in guard and resolver
- Implement peoper 404 handling with wildcard routes
- Use relative navigation where appropriate for maintainability
- Use
routerLinkActivefor navigation highlight instead of manual traking - Subscribe to route event for loading indicators and analytics
Common Pitfalls
- Not unsubscribing from route observables (though ActivatedRoute observables are automatically cleaned up)
- Placing wildcard route before any other route
- Not handeling async guard properly (returning Observable/Promise)
- Lazy loading without guard (security risk)
- Not considering route reuse strategy for complex applications
- Hardcoding URLs instead of using router methods
- Not testing route guards throughly
- Forgetting to provide router in testes
- Using snapshot when observable is needed (when component is reused)
- Not handeling query parameter changes reactively
Performance Tips
- USe lazy loading for large frature modules
- Implement custom preloading strategies
- Use OnPush change dection in routed components
- Minimize guard logic complexity
- Cache resolved data when appropriate
- Use trackBy in lists within routed components
- Implement route reuse strategy for expensive componets
- Avoid unnecessary redirects
- Use auxiliary routes for independent sections
- Monitor bundle sizes after lazy loading configuration
Comments
Post a Comment