Advanced API reference
Advanced router APIs, utilities, and lesser-known features for complex routing scenarios.
This guide covers advanced router APIs, utilities, and lesser-known features that provide powerful capabilities for complex routing scenarios. These APIs are particularly useful for building sophisticated applications with dynamic routing requirements.
Router Advanced APIs
Router State Management
router.routeTree
router.routeTree
Access the current route tree structure for advanced navigation analysis:
import { IRouter, RouteNode } from '@aurelia/router';
export class RouteAnalyzer {
private router = resolve(IRouter);
analyzeCurrentRoutes(): void {
const routeTree = this.router.routeTree;
console.log('Root route:', routeTree.root);
console.log('Active routes:', this.getActiveRoutes(routeTree.root));
console.log('Route depth:', this.getRouteDepth(routeTree.root));
}
private getActiveRoutes(node: RouteNode): RouteNode[] {
const active = [node];
node.children.forEach(child => {
active.push(...this.getActiveRoutes(child));
});
return active;
}
private getRouteDepth(node: RouteNode, depth = 0): number {
if (node.children.length === 0) return depth;
return Math.max(...node.children.map(child =>
this.getRouteDepth(child, depth + 1)
));
}
}
router.currentTr
(Current Transition)
router.currentTr
(Current Transition)Access detailed information about the current navigation transition:
export class TransitionMonitor {
private router = resolve(IRouter);
inspectCurrentTransition(): void {
const transition = this.router.currentTr;
console.log('Transition ID:', transition.id);
console.log('Trigger:', transition.trigger); // 'api', 'popstate', 'hashchange'
console.log('Previous instructions:', transition.prevInstructions);
console.log('Current instructions:', transition.instructions);
console.log('Final instructions:', transition.finalInstructions);
console.log('Options:', transition.options);
console.log('Route tree:', transition.routeTree);
console.log('Previous route tree:', transition.previousRouteTree);
}
async waitForTransition(): Promise<boolean> {
// Wait for current transition to complete
const transition = this.router.currentTr;
if (transition.promise) {
return await transition.promise;
}
return true;
}
}
Advanced Navigation Options
Navigation with Custom State
Store custom data with navigation history:
export class StatefulNavigation {
private router = resolve(IRouter);
async navigateWithState(route: string, customData: any): Promise<boolean> {
return this.router.load(route, {
state: {
timestamp: Date.now(),
userData: customData,
source: 'application'
}
});
}
async navigateWithContext(route: string, context: IRouteContext): Promise<boolean> {
return this.router.load(route, {
context: context, // Navigate relative to specific context
historyStrategy: 'push'
});
}
}
Advanced Query Parameter Handling
export class QueryParameterManager {
private router = resolve(IRouter);
private currentRoute = resolve(ICurrentRoute);
// Merge new query params with existing ones
async updateQueryParams(newParams: Record<string, string>): Promise<boolean> {
const currentQuery = this.currentRoute.query;
const mergedParams = new URLSearchParams(currentQuery);
Object.entries(newParams).forEach(([key, value]) => {
if (value === null || value === undefined) {
mergedParams.delete(key);
} else {
mergedParams.set(key, value);
}
});
return this.router.load(this.currentRoute.path, {
queryParams: Object.fromEntries(mergedParams),
historyStrategy: 'replace' // Don't create new history entry
});
}
// Batch query parameter updates
async batchUpdateQueryParams(updates: Array<{key: string, value: string | null}>): Promise<boolean> {
const currentQuery = this.currentRoute.query;
const newParams = new URLSearchParams(currentQuery);
updates.forEach(({key, value}) => {
if (value === null) {
newParams.delete(key);
} else {
newParams.set(key, value);
}
});
return this.router.load(this.currentRoute.path, {
queryParams: Object.fromEntries(newParams),
historyStrategy: 'replace'
});
}
}
Advanced Route Configuration
Dynamic Route Configuration
Create routes programmatically at runtime:
export class DynamicRouteManager {
private router = resolve(IRouter);
async addDynamicRoute(routeConfig: IChildRouteConfig, context: IRouteContext): Promise<void> {
// Get the route configuration context
const routeConfigContext = context.routeConfigContext;
// Add new route to existing configuration
const newRoutes = [...routeConfigContext.config.routes || [], routeConfig];
// Update configuration (this is a simplified example)
// In practice, you'd need to handle this through proper route tree updates
console.log('Adding dynamic route:', routeConfig);
}
createConditionalRoute(condition: () => boolean): IChildRouteConfig {
return {
path: 'conditional',
component: condition() ? ComponentA : ComponentB
};
}
}
Route Data and Metadata
Advanced usage of route data for feature flags, permissions, and metadata:
export interface RouteMetadata {
requiresAuth?: boolean;
permissions?: string[];
feature?: string;
analytics?: {
category: string;
action: string;
};
breadcrumb?: {
label: string;
icon?: string;
};
}
@route({
routes: [
{
path: 'admin',
component: AdminComponent,
data: {
requiresAuth: true,
permissions: ['admin.access'],
feature: 'admin-panel',
breadcrumb: { label: 'Administration', icon: 'settings' }
} as RouteMetadata
}
]
})
export class App {}
// Access route metadata
export class MetadataReader {
private currentRoute = resolve(ICurrentRoute);
getRouteMetadata(): RouteMetadata | null {
const paramInfo = this.currentRoute.parameterInformation[0];
return paramInfo?.config?.data as RouteMetadata || null;
}
checkPermissions(): boolean {
const metadata = this.getRouteMetadata();
if (!metadata?.permissions) return true;
return metadata.permissions.every(permission =>
this.authService.hasPermission(permission)
);
}
}
Advanced Viewport Features
Viewport Agents
Direct interaction with viewport agents for custom behaviors:
export class AdvancedViewportController {
private routeContext = resolve(IRouteContext);
getViewportAgents(): ViewportAgent[] {
return this.routeContext.getAvailableViewportAgents();
}
async loadComponentInSpecificViewport(
component: any,
viewportName: string
): Promise<void> {
const agents = this.getViewportAgents();
const targetAgent = agents.find(agent =>
agent.viewport.name === viewportName
);
if (targetAgent) {
// Advanced viewport manipulation
console.log('Loading component in viewport:', viewportName);
// Implementation would involve direct viewport agent manipulation
}
}
monitorViewportChanges(): void {
const agents = this.getViewportAgents();
agents.forEach(agent => {
// Monitor viewport state changes
console.log('Viewport agent:', agent.viewport.name);
});
}
}
Named Viewport Coordination
Coordinate multiple named viewports:
export class MultiViewportController {
private router = resolve(IRouter);
async loadSiblingComponents(components: {
[viewportName: string]: any
}): Promise<boolean> {
const instructions = Object.entries(components).map(([viewport, component]) => ({
component,
viewport
}));
return this.router.load(instructions);
}
async loadHierarchicalComponents(hierarchy: {
parent: any;
children: { [viewport: string]: any };
}): Promise<boolean> {
// Load parent first, then children
const success = await this.router.load(hierarchy.parent);
if (!success) return false;
// Load children into named viewports
return this.loadSiblingComponents(hierarchy.children);
}
}
Router Events and Hooks Advanced Usage
Custom Router Event Publisher
Create custom router events for application-specific needs:
export class CustomRouterEvents {
private routerEvents = resolve(IRouterEvents);
private eventAggregator = resolve(IEventAggregator);
publishCustomNavigationEvent(eventType: string, data: any): void {
const customEvent = {
name: `au:app:${eventType}`,
timestamp: Date.now(),
data
};
// Publish through event aggregator
this.eventAggregator.publish(customEvent.name, customEvent);
}
subscribeToNavigationPattern(
pattern: RegExp,
callback: (event: any) => void
): IDisposable {
return this.routerEvents.subscribe('au:router:navigation-end', (event) => {
const path = event.finalInstructions.toPath();
if (pattern.test(path)) {
callback(event);
}
});
}
}
Advanced Lifecycle Hook Composition
Compose multiple lifecycle behaviors:
export class CompositeLifecycleHook implements IRouteViewModel {
private hooks: IRouteViewModel[] = [];
constructor(...hooks: IRouteViewModel[]) {
this.hooks = hooks;
}
async canLoad(params: Params, next: RouteNode, current: RouteNode | null): Promise<boolean> {
// Run all canLoad hooks
for (const hook of this.hooks) {
if (hook.canLoad) {
const result = await hook.canLoad(params, next, current);
if (!result) return false;
}
}
return true;
}
async loading(params: Params, next: RouteNode, current: RouteNode | null): Promise<void> {
// Run all loading hooks in parallel
const promises = this.hooks
.filter(hook => hook.loading)
.map(hook => hook.loading!(params, next, current));
await Promise.all(promises);
}
}
// Usage
export class MyComponent extends CompositeLifecycleHook {
constructor() {
super(
new AuthenticationHook(),
new AnalyticsHook(),
new DataLoadingHook()
);
}
}
Advanced Path Generation and Parsing
Custom Path Generators
Generate complex paths with custom logic:
export class AdvancedPathGenerator {
private router = resolve(IRouter);
async generatePathWithFallback(
primary: NavigationInstruction,
fallback: NavigationInstruction
): Promise<string> {
try {
return await this.router.generatePath(primary);
} catch {
return await this.router.generatePath(fallback);
}
}
generatePathsForPermutations(
baseRoute: string,
params: Record<string, string[]>
): Promise<string[]> {
const permutations = this.generatePermutations(params);
return Promise.all(
permutations.map(permutation =>
this.router.generatePath(baseRoute, { params: permutation })
)
);
}
private generatePermutations(params: Record<string, string[]>): Record<string, string>[] {
const keys = Object.keys(params);
if (keys.length === 0) return [{}];
const [firstKey, ...restKeys] = keys;
const firstValues = params[firstKey];
const restParams = Object.fromEntries(restKeys.map(key => [key, params[key]]));
const restPermutations = this.generatePermutations(restParams);
return firstValues.flatMap(value =>
restPermutations.map(perm => ({ [firstKey]: value, ...perm }))
);
}
}
Route Pattern Matching
Advanced route pattern matching and validation:
export class RoutePatternMatcher {
private router = resolve(IRouter);
matchesPattern(path: string, pattern: string): boolean {
// Convert Aurelia route pattern to RegExp
const regexPattern = pattern
.replace(/:[^/]+/g, '([^/]+)') // Required params
.replace(/:[^/]+\?/g, '([^/]*)') // Optional params
.replace(/\*/g, '(.*)') // Wildcard
.replace(/\//g, '\\/'); // Escape slashes
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(path);
}
extractParameters(path: string, pattern: string): Record<string, string> {
const paramNames = this.extractParameterNames(pattern);
const values = this.extractParameterValues(path, pattern);
return Object.fromEntries(
paramNames.map((name, index) => [name, values[index] || ''])
);
}
private extractParameterNames(pattern: string): string[] {
const matches = pattern.match(/:([^/?]+)/g) || [];
return matches.map(match => match.slice(1).replace('?', ''));
}
private extractParameterValues(path: string, pattern: string): string[] {
const regexPattern = pattern
.replace(/:[^/]+\?/g, '([^/]*)')
.replace(/:[^/]+/g, '([^/]+)')
.replace(/\*/g, '(.*)')
.replace(/\//g, '\\/');
const regex = new RegExp(`^${regexPattern}$`);
const match = path.match(regex);
return match ? match.slice(1) : [];
}
}
Performance Optimization APIs
Route Preloading
Preload routes for better performance:
export class RoutePreloader {
private router = resolve(IRouter);
private preloadedComponents = new Map<string, any>();
async preloadRoute(route: string): Promise<void> {
if (this.preloadedComponents.has(route)) return;
try {
// Generate instructions without navigating
const instructions = await this.router.createViewportInstructions(route, null, true);
// Pre-load component modules
await this.preloadInstructionComponents(instructions);
console.log(`Preloaded route: ${route}`);
} catch (error) {
console.warn(`Failed to preload route ${route}:`, error);
}
}
private async preloadInstructionComponents(instructions: any): Promise<void> {
// Implementation would traverse instruction tree and preload components
console.log('Preloading components for instructions:', instructions);
}
async preloadCriticalRoutes(routes: string[]): Promise<void> {
await Promise.all(routes.map(route => this.preloadRoute(route)));
}
}
Route Caching
Cache route resolution for performance:
export class RouteCache {
private cache = new Map<string, any>();
private maxAge = 5 * 60 * 1000; // 5 minutes
getCachedRoute(key: string): any | null {
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() - cached.timestamp > this.maxAge) {
this.cache.delete(key);
return null;
}
return cached.data;
}
setCachedRoute(key: string, data: any): void {
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
clearCache(): void {
this.cache.clear();
}
getStats(): { size: number; keys: string[] } {
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}
Best Practices for Advanced Usage
1. Resource Management
// ✅ Good - Proper cleanup of advanced router features
export class AdvancedRouterFeature {
private subscriptions: IDisposable[] = [];
private timers: number[] = [];
initialize() {
const subscription = this.routerEvents.subscribe(/* ... */);
this.subscriptions.push(subscription);
}
dispose() {
this.subscriptions.forEach(sub => sub.dispose());
this.timers.forEach(timer => clearTimeout(timer));
}
}
2. Error Handling in Advanced Scenarios
// ✅ Good - Robust error handling for complex operations
export class RobustRouterOperations {
async performComplexNavigation(): Promise<boolean> {
try {
const preloadSuccess = await this.preloadRequiredComponents();
if (!preloadSuccess) return false;
const navigationSuccess = await this.router.load(/* ... */);
if (!navigationSuccess) return false;
await this.performPostNavigationTasks();
return true;
} catch (error) {
this.handleNavigationError(error);
return false;
}
}
}
3. Type Safety
// ✅ Good - Strong typing for advanced router usage
interface NavigationResult<T = any> {
success: boolean;
data?: T;
error?: Error;
}
export class TypeSafeRouterOperations {
async navigateWithResult<T>(
route: string,
options?: INavigationOptions
): Promise<NavigationResult<T>> {
try {
const success = await this.router.load(route, options);
return { success };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error(String(error))
};
}
}
}
This advanced API reference provides developers with comprehensive guidance on leveraging the router's more sophisticated features for complex routing scenarios.
Last updated
Was this helpful?