Route state management, navigation events, and accessing current route information. Provides comprehensive access to routing state and navigation lifecycle.
Provides access to information about a route associated with a component loaded in an outlet.
/**
* Service providing access to route information for component loaded in outlet
* Injectable service for accessing current route data
*/
class ActivatedRoute {
/** Observable of URL segments matched by route */
url: Observable<UrlSegment[]>;
/** Observable of matrix parameters scoped to route */
params: Observable<Params>;
/** Observable of query parameters shared by all routes */
queryParams: Observable<Params>;
/** Observable of URL fragment shared by all routes */
fragment: Observable<string | null>;
/** Observable of static and resolved data */
data: Observable<Data>;
/** Observable of resolved route title */
title: Observable<string | undefined>;
/** Current snapshot of route */
snapshot: ActivatedRouteSnapshot;
/** Configuration used to match route */
routeConfig: Route | null;
/** Name of outlet (constant) */
outlet: string;
/** Component of route (constant) */
component: Type<any> | null;
/** Observable map of required and optional parameters */
paramMap: Observable<ParamMap>;
/** Observable map of query parameters */
queryParamMap: Observable<ParamMap>;
// Tree navigation properties
/** Root of router state */
root: ActivatedRoute;
/** Parent route */
parent: ActivatedRoute | null;
/** First child route */
firstChild: ActivatedRoute | null;
/** Child routes array */
children: ActivatedRoute[];
/** Path from root to current route */
pathFromRoot: ActivatedRoute[];
}ActivatedRoute Usage Examples:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user',
template: `
<h1>User Profile</h1>
<div>User ID: {{userId}}</div>
<div>Tab: {{activeTab}}</div>
<div *ngIf="userData">
<p>Name: {{userData.name}}</p>
<p>Email: {{userData.email}}</p>
</div>
`
})
export class UserComponent implements OnInit {
userId: string;
activeTab: string;
userData: any;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Get route parameters (snapshot - one-time read)
this.userId = this.route.snapshot.paramMap.get('id')!;
// Subscribe to parameter changes (reactive)
this.route.paramMap.subscribe(params => {
this.userId = params.get('id')!;
this.loadUserData(this.userId);
});
// Get query parameters
this.route.queryParamMap.subscribe(queryParams => {
this.activeTab = queryParams.get('tab') || 'profile';
});
// Get resolved data
this.route.data.subscribe(data => {
this.userData = data['user'];
});
// Get fragment
this.route.fragment.subscribe(fragment => {
if (fragment) {
this.scrollToSection(fragment);
}
});
// Access parent route data
const parentData = this.route.parent?.snapshot.data;
// Navigate through route tree
const rootRoute = this.route.root;
const childRoutes = this.route.children;
}
private loadUserData(userId: string) {
// Load user data logic
}
private scrollToSection(fragment: string) {
// Scroll to fragment logic
}
}Immutable snapshot of activated route at particular moment in time.
/**
* Immutable snapshot of activated route at particular moment in time
*/
class ActivatedRouteSnapshot {
/** URL segments matched by route */
url: UrlSegment[];
/** Matrix parameters scoped to route */
params: Params;
/** Query parameters shared by all routes */
queryParams: Params;
/** URL fragment shared by all routes */
fragment: string | null;
/** Static and resolved data of route */
data: Data;
/** Name of outlet */
outlet: string;
/** Component of route */
component: Type<any> | null;
/** Configuration used to match route */
routeConfig: Route | null;
/** Resolved route title (getter) */
readonly title: string | undefined;
/** Map of required and optional parameters */
paramMap: ParamMap;
/** Map of query parameters */
queryParamMap: ParamMap;
// Tree navigation properties
/** Root of router state snapshot */
root: ActivatedRouteSnapshot;
/** Parent route snapshot */
parent: ActivatedRouteSnapshot | null;
/** First child route snapshot */
firstChild: ActivatedRouteSnapshot | null;
/** Child routes array */
children: ActivatedRouteSnapshot[];
/** Path from root to current route */
pathFromRoot: ActivatedRouteSnapshot[];
}ActivatedRouteSnapshot Usage Examples:
import { ActivatedRouteSnapshot } from '@angular/router';
// In guards
export const userGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state) => {
const userId = route.paramMap.get('id');
const userRole = route.data['requiredRole'];
const parentData = route.parent?.data;
// Guard logic using snapshot data
return checkUserAccess(userId, userRole);
};
// In resolvers
export const userResolver: ResolveFn<User> = (route: ActivatedRouteSnapshot, state) => {
const userId = route.paramMap.get('id')!;
const includeDetails = route.queryParamMap.get('details') === 'true';
return userService.getUser(userId, { includeDetails });
};
// In components (accessing snapshot)
@Component({})
export class MyComponent {
constructor(private route: ActivatedRoute) {
const snapshot = this.route.snapshot;
// Access snapshot data immediately
const currentParams = snapshot.paramMap;
const currentQuery = snapshot.queryParamMap;
const staticData = snapshot.data;
// Navigate through snapshot tree
const parentSnapshot = snapshot.parent;
const childSnapshots = snapshot.children;
const routePath = snapshot.pathFromRoot;
}
}Represents the state of router as tree of activated routes.
/**
* Represents the state of router as tree of activated routes
* Extends Tree<ActivatedRoute>
*/
class RouterState extends Tree<ActivatedRoute> {
/** Current snapshot of router state */
snapshot: RouterStateSnapshot;
}
/**
* Snapshot representation of router state
* Extends Tree<ActivatedRouteSnapshot>
*/
class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
/** URL from which snapshot was created */
url: string;
}
/**
* Base tree structure for router state
*/
abstract class Tree<T> {
/** Root node of the tree */
root: T;
}RouterState Usage Examples:
import { Router, RouterState, RouterStateSnapshot } from '@angular/router';
@Component({})
export class AppComponent {
constructor(private router: Router) {}
// Access current router state
getCurrentState(): RouterState {
return this.router.routerState;
}
// Access router state snapshot
getStateSnapshot(): RouterStateSnapshot {
const snapshot = this.router.routerState.snapshot;
// Get current URL
const currentUrl = snapshot.url;
// Access root route
const rootRoute = snapshot.root;
// Traverse route tree
this.traverseRoutes(rootRoute);
return snapshot;
}
private traverseRoutes(route: ActivatedRouteSnapshot) {
// Process current route
console.log('Route:', route.url, route.params);
// Process children
route.children.forEach(child => {
this.traverseRoutes(child);
});
}
}
// In title strategy
export class CustomTitleStrategy extends TitleStrategy {
updateTitle(routerState: RouterStateSnapshot): void {
const title = this.buildTitle(routerState);
if (title) {
document.title = `My App - ${title}`;
}
}
}Observable stream of router navigation events for tracking navigation lifecycle.
/**
* Union type of all router events
*/
type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError |
RoutesRecognized | ResolveStart | ResolveEnd | GuardsCheckStart |
GuardsCheckEnd | RouteConfigLoadStart | RouteConfigLoadEnd |
ChildActivationStart | ChildActivationEnd | ActivationStart |
ActivationEnd | Scroll | NavigationSkipped;
/**
* Base class for router events
*/
abstract class RouterEvent {
/** Unique navigation ID */
id: number;
/** Destination URL */
url: string;
}
/**
* Event fired when navigation starts
*/
class NavigationStart extends RouterEvent {
type: EventType.NavigationStart;
/** What triggered the navigation */
navigationTrigger?: 'imperative' | 'popstate' | 'hashchange';
/** State restored from browser history */
restoredState?: {[k: string]: any; navigationId: number} | null;
}
/**
* Event fired when navigation ends successfully
*/
class NavigationEnd extends RouterEvent {
type: EventType.NavigationEnd;
/** URL after redirects */
urlAfterRedirects: string;
}
/**
* Event fired when navigation is canceled
*/
class NavigationCancel extends RouterEvent {
type: EventType.NavigationCancel;
/** Reason for cancellation */
reason: string;
/** Cancellation code */
code?: NavigationCancellationCode;
}
/**
* Event fired when navigation is skipped
*/
class NavigationSkipped extends RouterEvent {
type: EventType.NavigationSkipped;
/** Reason for skipping */
reason: string;
/** Skip code */
code?: NavigationSkippedCode;
}
/**
* Event fired when navigation encounters an error
*/
class NavigationError extends RouterEvent {
type: EventType.NavigationError;
/** Navigation error */
error: any;
/** Target router state when error occurred */
target?: RouterStateSnapshot;
}
/**
* Event fired when routes are recognized
*/
class RoutesRecognized extends RouterEvent {
type: EventType.RoutesRecognized;
/** URL after redirects */
urlAfterRedirects: string;
/** Router state after route recognition */
state: RouterStateSnapshot;
}
/**
* Event fired when guard checks start
*/
class GuardsCheckStart extends RouterEvent {
type: EventType.GuardsCheckStart;
/** URL after redirects */
urlAfterRedirects: string;
/** Router state being checked */
state: RouterStateSnapshot;
}
/**
* Event fired when guard checks end
*/
class GuardsCheckEnd extends RouterEvent {
type: EventType.GuardsCheckEnd;
/** URL after redirects */
urlAfterRedirects: string;
/** Router state after checks */
state: RouterStateSnapshot;
/** Whether route should activate */
shouldActivate: boolean;
}
/**
* Event fired when route configuration loading starts
*/
class RouteConfigLoadStart {
type: EventType.RouteConfigLoadStart;
/** Route being loaded */
route: Route;
}
/**
* Event fired when route configuration loading ends
*/
class RouteConfigLoadEnd {
type: EventType.RouteConfigLoadEnd;
/** Route that was loaded */
route: Route;
}Navigation Events Usage Examples:
import { Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({})
export class AppComponent implements OnInit {
isLoading = false;
constructor(private router: Router) {}
ngOnInit() {
// Listen to all navigation events
this.router.events.subscribe(event => {
console.log('Navigation event:', event);
});
// Loading indicator
this.router.events.pipe(
filter(event => event instanceof NavigationStart ||
event instanceof NavigationEnd ||
event instanceof NavigationCancel ||
event instanceof NavigationError)
).subscribe(event => {
if (event instanceof NavigationStart) {
this.isLoading = true;
} else {
this.isLoading = false;
}
});
// Track successful navigations
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe((event: NavigationEnd) => {
// Analytics tracking
this.trackPageView(event.urlAfterRedirects);
});
// Handle navigation errors
this.router.events.pipe(
filter(event => event instanceof NavigationError)
).subscribe((event: NavigationError) => {
console.error('Navigation error:', event.error);
this.handleNavigationError(event);
});
// Track route data loading
this.router.events.pipe(
filter(event => event instanceof ResolveStart || event instanceof ResolveEnd)
).subscribe(event => {
if (event instanceof ResolveStart) {
console.log('Starting data resolution');
} else {
console.log('Data resolution complete');
}
});
}
private trackPageView(url: string) {
// Analytics implementation
}
private handleNavigationError(event: NavigationError) {
// Error handling logic
}
}
// Service for navigation tracking
@Injectable({
providedIn: 'root'
})
export class NavigationService {
private navigationHistory: string[] = [];
constructor(private router: Router) {
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe((event: NavigationEnd) => {
this.navigationHistory.push(event.urlAfterRedirects);
// Keep only last 10 navigations
if (this.navigationHistory.length > 10) {
this.navigationHistory.shift();
}
});
}
getNavigationHistory(): string[] {
return [...this.navigationHistory];
}
getPreviousUrl(): string | null {
return this.navigationHistory.length > 1
? this.navigationHistory[this.navigationHistory.length - 2]
: null;
}
}Map interface for accessing route parameters with type safety.
/**
* Map interface for accessing route parameters
*/
interface ParamMap {
/** Check if parameter exists */
has(name: string): boolean;
/** Get single parameter value (null if not found) */
get(name: string): string | null;
/** Get all values for parameter (for multi-value params) */
getAll(name: string): string[];
/** All parameter names */
readonly keys: string[];
}ParamMap Usage Examples:
import { ActivatedRoute, ParamMap } from '@angular/router';
@Component({})
export class ProductComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit() {
// Using paramMap (reactive)
this.route.paramMap.subscribe((params: ParamMap) => {
if (params.has('id')) {
const productId = params.get('id')!;
this.loadProduct(productId);
}
// Handle multiple values (uncommon)
const tags = params.getAll('tag');
// Get all parameter names
const allParams = params.keys;
console.log('Available parameters:', allParams);
});
// Using queryParamMap
this.route.queryParamMap.subscribe((queryParams: ParamMap) => {
const page = queryParams.get('page') || '1';
const sort = queryParams.get('sort') || 'name';
const filters = queryParams.getAll('filter');
this.loadProductList({
page: parseInt(page),
sort,
filters
});
});
// Snapshot access (one-time read)
const snapshot = this.route.snapshot;
const immediateId = snapshot.paramMap.get('id');
const immediateQuery = snapshot.queryParamMap.get('search');
}
private loadProduct(id: string) {
// Product loading logic
}
private loadProductList(options: any) {
// Product list loading logic
}
}type Params = {[key: string]: any};
type Data = {[key: string | symbol]: any};
enum EventType {
NavigationStart,
NavigationEnd,
NavigationCancel,
NavigationError,
RoutesRecognized,
ResolveStart,
ResolveEnd,
GuardsCheckStart,
GuardsCheckEnd,
RouteConfigLoadStart,
RouteConfigLoadEnd,
ChildActivationStart,
ChildActivationEnd,
ActivationStart,
ActivationEnd,
Scroll,
NavigationSkipped
}
enum NavigationCancellationCode {
Redirect,
SupersededByNewNavigation,
NoDataFromResolver,
GuardRejected,
Aborted
}
enum NavigationSkippedCode {
IgnoredSameUrlNavigation,
IgnoredByUrlHandlingStrategy
}
type NavigationTrigger = 'imperative' | 'popstate' | 'hashchange';
/**
* Scroll event fired during navigation
*/
class Scroll {
type: EventType.Scroll;
/** Navigation event that triggered scroll */
routerEvent: NavigationEnd | NavigationSkipped;
/** Scroll position */
position: [number, number] | null;
/** Scroll anchor */
anchor: string | null;
}
/**
* Events fired during route resolution
*/
class ResolveStart extends RouterEvent {
type: EventType.ResolveStart;
urlAfterRedirects: string;
state: RouterStateSnapshot;
}
class ResolveEnd extends RouterEvent {
type: EventType.ResolveEnd;
urlAfterRedirects: string;
state: RouterStateSnapshot;
}
/**
* Events fired during route activation
*/
/**
* Event fired when route activation starts
*/
class ActivationStart extends RouterEvent {
type: EventType.ActivationStart;
snapshot: ActivatedRouteSnapshot;
}
/**
* Event fired when route activation ends
*/
class ActivationEnd extends RouterEvent {
type: EventType.ActivationEnd;
snapshot: ActivatedRouteSnapshot;
}
/**
* Event fired when child activation starts
*/
class ChildActivationStart extends RouterEvent {
type: EventType.ChildActivationStart;
snapshot: ActivatedRouteSnapshot;
}
/**
* Event fired when child activation ends
*/
class ChildActivationEnd extends RouterEvent {
type: EventType.ChildActivationEnd;
snapshot: ActivatedRouteSnapshot;
}