or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mddirectives.mdindex.mdnavigation-state.mdroute-config.mdrouting-core.mdurl-handling.md
tile.json

navigation-state.mddocs/

Navigation & State

Route state management, navigation events, and accessing current route information. Provides comprehensive access to routing state and navigation lifecycle.

Capabilities

ActivatedRoute Service

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
  }
}

ActivatedRouteSnapshot

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;
  }
}

RouterState & RouterStateSnapshot

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}`;
    }
  }
}

Navigation Events

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;
  }
}

ParamMap Interface

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
  }
}

Types

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;
}