or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

directives.mdindex.mdparameter-management.mdstate-configuration.mdstate-navigation.mdtransition-hooks.mdurl-management.mdview-management.md
tile.json

transition-hooks.mddocs/

Transition Hooks

Transition lifecycle management with hooks for controlling state transitions, implementing cross-cutting concerns, and handling asynchronous operations during navigation. Hooks provide powerful interception points throughout the transition lifecycle.

Capabilities

TransitionService

Service for registering global transition hooks that apply to all matching transitions.

/**
 * Transition service for registering global lifecycle hooks
 */
interface TransitionService {
  /** Hook executed before transition starts */
  onBefore(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
  /** Hook executed when transition starts */
  onStart(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
  /** Hook executed when entering states */
  onEnter(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;
  /** Hook executed when retaining states */
  onRetain(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;
  /** Hook executed when exiting states */
  onExit(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;
  /** Hook executed when transition finishes */
  onFinish(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
  /** Hook executed on successful transition */
  onSuccess(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
  /** Hook executed on transition error */
  onError(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
}

Usage Examples:

angular.module('myApp').run(['$transitions', function($transitions) {
  
  // Authentication check before any transition
  $transitions.onBefore({}, function(trans) {
    const requiresAuth = trans.to().data && trans.to().data.requiresAuth;
    if (requiresAuth && !AuthService.isAuthenticated()) {
      return trans.router.stateService.target('login');
    }
  });
  
  // Loading indicator during transitions
  $transitions.onStart({}, function(trans) {
    LoadingService.show();
  });
  
  $transitions.onFinish({}, function(trans) {
    LoadingService.hide();
  });
  
  // Log state changes
  $transitions.onSuccess({}, function(trans) {
    console.log('Navigated from', trans.from().name, 'to', trans.to().name);
  });
}]);

Hook Registration

Register hooks with matching criteria and optional configuration.

Hook Matching Criteria

/**
 * Criteria for matching transitions to hook execution
 */
interface HookMatchCriteria {
  /** Match transitions TO these states */
  to?: string | string[] | StateDeclaration | StateDeclaration[] | Function;
  /** Match transitions FROM these states */
  from?: string | string[] | StateDeclaration | StateDeclaration[] | Function;
  /** Match transitions between states (from->to pairs) */
  between?: string | string[] | StateDeclaration | StateDeclaration[] | Function;
  /** Custom matching function */
  when?: (transition: Transition) => boolean;
}

/**
 * Hook registration options
 */
interface HookRegOptions {
  /** Hook priority (higher numbers run first) */
  priority?: number;
  /** Bind hook function to specific context */
  bind?: any;
  /** Invoke hook for each matching state (state hooks only) */
  invokeEachState?: boolean;
  /** Invoke hook once per transition (state hooks only) */
  invokeOnce?: boolean;
}

Usage Examples:

// Hook for specific target state
$transitions.onBefore({ to: 'admin.**' }, function(trans) {
  if (!hasAdminRole()) {
    return trans.router.stateService.target('unauthorized');
  }
});

// Hook for specific source state
$transitions.onExit({ from: 'editor' }, function(trans, state) {
  if (hasUnsavedChanges()) {
    return confirm('Discard unsaved changes?');
  }
});

// Hook for state transitions
$transitions.onBefore({ 
  from: 'public.**', 
  to: 'private.**' 
}, function(trans) {
  return authenticateUser();
});

// Custom matching criteria
$transitions.onStart({
  when: function(transition) {
    return transition.to().data && transition.to().data.requiresAnalytics;
  }
}, function(trans) {
  AnalyticsService.trackPageView(trans.to().name);
});

// Hook with priority and options
$transitions.onBefore({}, function(trans) {
  // This runs first due to higher priority
  return validateTransition(trans);
}, { priority: 100 });

Hook Types and Timing

Different hook types execute at specific points in the transition lifecycle.

Before Hooks

Execute before the transition starts, can prevent or redirect the transition.

/**
 * Before hook - executes before transition starts
 * @param criteria - Matching criteria for transitions
 * @param callback - Hook function  
 * @param options - Registration options
 * @returns Deregistration function
 */
onBefore(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;

type TransitionHookFn = (transition: Transition) => HookResult;
type HookResult = boolean | TargetState | void | Promise<boolean | TargetState | void>;

Usage Examples:

// Prevent transition based on condition
$transitions.onBefore({ to: 'restricted' }, function(trans) {
  if (!hasPermission()) {
    return false; // Cancel transition
  }
});

// Redirect to different state
$transitions.onBefore({ to: 'oldRoute' }, function(trans) {
  return trans.router.stateService.target('newRoute', trans.params());
});

// Async validation
$transitions.onBefore({ to: 'secure.**' }, function(trans) {
  return AuthService.validateToken().then(function(valid) {
    if (!valid) {
      return trans.router.stateService.target('login');
    }
  });
});

Start Hooks

Execute when the transition starts, cannot prevent the transition.

/**
 * Start hook - executes when transition starts
 */
onStart(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;

Usage Examples:

// Show loading indicator
$transitions.onStart({}, function(trans) {
  LoadingIndicator.show();
});

// Log transition start
$transitions.onStart({}, function(trans) {
  console.log('Starting transition to:', trans.to().name);
});

// Track navigation timing
$transitions.onStart({}, function(trans) {
  trans._startTime = Date.now();
});

State Lifecycle Hooks

Execute for individual states during transition (onExit, onRetain, onEnter).

/**
 * State lifecycle hooks - execute for individual states
 */
onExit(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;
onRetain(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;
onEnter(criteria: HookMatchCriteria, callback: TransitionStateHookFn, options?: HookRegOptions): Function;

type TransitionStateHookFn = (transition: Transition, state: StateDeclaration) => HookResult;

Usage Examples:

// Clean up resources when exiting states
$transitions.onExit({ from: 'editor' }, function(trans, state) {
  return EditorService.cleanup();
});

// Initialize state-specific services
$transitions.onEnter({ to: 'dashboard' }, function(trans, state) {
  DashboardService.initialize();
});

// Handle state retention
$transitions.onRetain({ to: 'users.**' }, function(trans, state) {
  console.log('Staying in users section:', state.name);
});

// Hook with state-specific logic
$transitions.onEnter({}, function(trans, state) {
  if (state.data && state.data.trackEntry) {
    AnalyticsService.trackStateEntry(state.name);
  }
}, { invokeEachState: true });

Finish Hooks

Execute when the transition completes (success or failure).

/**
 * Finish hook - executes when transition completes
 */
onFinish(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
onSuccess(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;
onError(criteria: HookMatchCriteria, callback: TransitionHookFn, options?: HookRegOptions): Function;

Usage Examples:

// Hide loading indicator
$transitions.onFinish({}, function(trans) {
  LoadingIndicator.hide();
});

// Log successful transitions
$transitions.onSuccess({}, function(trans) {
  const elapsed = Date.now() - trans._startTime;
  console.log(`Transition to ${trans.to().name} completed in ${elapsed}ms`);
});

// Handle transition errors
$transitions.onError({}, function(trans) {
  console.error('Transition failed:', trans.error());
  NotificationService.showError('Navigation failed');
});

Transition Object

The Transition object provides information about the current transition.

/**
 * Transition object containing transition information and methods
 */
interface Transition {
  /** Get source state */
  from(): StateDeclaration;
  /** Get target state */
  to(): StateDeclaration;
  /** Get transition parameters */
  params(): RawParams;
  /** Get injector for resolve data */
  injector(): UIInjector;
  /** Get transition options */
  options(): TransitionOptions;
  /** Transition promise */
  promise: Promise<any>;
  /** Transition success status */
  success: boolean;
  /** Get transition error if failed */
  error(): any;
  /** Get transition router instance */
  router: UIRouter;
}

Usage Examples:

$transitions.onBefore({}, function(trans) {
  console.log('From:', trans.from().name);
  console.log('To:', trans.to().name);
  console.log('Params:', trans.params());
  
  // Access resolve data
  const injector = trans.injector();
  if (injector) {
    const userData = injector.get('user', null);
    console.log('User data:', userData);
  }
  
  // Check transition options
  if (trans.options().reload) {
    console.log('This is a reload transition');
  }
});

Hook Patterns

Common patterns for using transition hooks effectively.

Authentication Guard

// Global authentication guard
$transitions.onBefore({}, function(trans) {
  const requiresAuth = trans.to().data && trans.to().data.requiresAuth;
  const isAuthenticated = AuthService.isAuthenticated();
  
  if (requiresAuth && !isAuthenticated) {
    // Store intended destination
    SessionService.setReturnUrl(trans.to().name, trans.params());
    return trans.router.stateService.target('login');
  }
});

// Redirect after login
$transitions.onSuccess({ to: 'login' }, function(trans) {
  const returnUrl = SessionService.getReturnUrl();
  if (returnUrl) {
    SessionService.clearReturnUrl();
    return trans.router.stateService.go(returnUrl.state, returnUrl.params);
  }
});

Permission-Based Access Control

$transitions.onBefore({}, function(trans) {
  const requiredPermissions = trans.to().data && trans.to().data.permissions;
  if (requiredPermissions) {
    const userPermissions = AuthService.getUserPermissions();
    const hasAccess = requiredPermissions.every(permission => 
      userPermissions.includes(permission)
    );
    
    if (!hasAccess) {
      return trans.router.stateService.target('unauthorized');
    }
  }
});

Data Preloading

// Preload critical data
$transitions.onStart({ to: 'dashboard' }, function(trans) {
  return Promise.all([
    UserService.getCurrentUser(),
    NotificationService.getUnreadCount(),
    ConfigService.getAppConfig()
  ]).then(function([user, notifications, config]) {
    // Data available for dashboard
    CacheService.setDashboardData({ user, notifications, config });
  });
});

Error Boundary

// Global error handling
$transitions.onError({}, function(trans) {
  const error = trans.error();
  
  // Log error details
  console.error('Transition error:', {
    from: trans.from().name,
    to: trans.to().name,
    error: error
  });
  
  // Handle specific error types
  if (error.type === 'RESOLVE_ERROR') {
    NotificationService.showError('Failed to load data');
    return trans.router.stateService.go('error.resolve');
  } else if (error.type === 'INVALID_PARAMS') {
    NotificationService.showError('Invalid parameters');
    return trans.router.stateService.go('error.params');
  }
  
  // Fallback error handling
  return trans.router.stateService.go('error.general');
});

Hook Deregistration

All hook registration methods return a deregistration function.

// Register hook and get deregistration function
const deregister = $transitions.onBefore({}, function(trans) {
  // Hook logic
});

// Later, remove the hook
deregister();

// Conditional hook registration
let authHook;
if (requiresAuthentication) {
  authHook = $transitions.onBefore({}, authenticationGuard);
}

// Clean up when no longer needed
function cleanup() {
  if (authHook) {
    authHook(); // Deregister
    authHook = null;
  }
}