Routing

AngularDart uses the angulardart_router package for client-side routing.

Setup

Add the router dependency:

dependencies:
  angulardart: '>=9.0.0 <10.0.0'
  angulardart_router: '>=5.0.0 <6.0.0'

Basic Configuration

import 'package:angulardart/angular.dart';
import 'package:angulardart_router/angulardart_router.dart';

@Component(
  selector: 'app-component',
  template: '<nav>'
      '<a [routerLink]="[\'/\']">Home</a>'
      '<a [routerLink]="[\'/about\']">About</a>'
      '<a [routerLink]="[\'/users\']">Users</a>'
      '</nav>'
      '<router-outlet [routes]="routes"></router-outlet>',
  directives: [routerDirectives],
)
class AppComponent implements OnInit {
  final Router _router;
  List<RouteDefinition> routes = [];

  AppComponent(this._router);

  @override
  void ngOnInit() {
    routes = [
      RouteDefinition(
        path: '/',
        component: HomeComponentFactory,
        useAsDefault: true,
      ),
      RouteDefinition(
        path: '/about',
        component: AboutComponentFactory,
      ),
      RouteDefinition(
        path: '/users',
        component: UsersComponentFactory,
      ),
    ];
  }
}

Route Parameters

Define routes with parameters using :paramName:

routes = [
  RouteDefinition(path: '/users/:id', component: UserDetailComponentFactory),
];

Access parameters in components:

@Component(
  selector: 'user-detail',
  template: '<h1>{{ userId }}</h1>',
)
class UserDetailComponent implements OnInit {
  final Router _router;
  String? userId;

  UserDetailComponent(this._router);

  @override
  void ngOnInit() {
    final path = _router.current?.path ?? '';
    final segments = path.split('/');
    userId = segments.isNotEmpty ? segments.last : null;
  }
}

Query Parameters

Navigate with query parameters:

_router.navigate('/search', NavigationParams(
  queryParameters: {'q': 'dart', 'page': '1'},
));

Access query parameters:

final query = _router.current?.queryParameters['q'];

Route Guards

CanActivate

Protect routes from unauthorized access:

class AuthGuard implements CanActivate {
  final AuthService _authService;

  AuthGuard(this._authService);

  @override
  Future<bool> canActivate(
    RouterState? current,
    RouterState next,
  ) async {
    return _authService.isLoggedIn;
  }
}

// Usage in routes
routes = [
  RouteDefinition(
    path: '/admin',
    component: AdminComponentFactory,
    canActivate: [AuthGuard],
  ),
];

CanDeactivate

Prevent navigation away from unsaved changes:

class UnsavedChangesGuard implements CanDeactivate {
  @override
  Future<bool> canDeactivate(
    RouterState current,
    RouterState? next,
  ) async {
    // Show confirmation dialog
    return window.confirm('You have unsaved changes. Leave anyway?');
  }
}

Child Routes

Define nested routes:

routes = [
  RouteDefinition(
    path: '/dashboard',
    component: DashboardComponentFactory,
    children: [
      RouteDefinition(
        path: 'overview',
        component: OverviewComponentFactory,
      ),
      RouteDefinition(
        path: 'analytics',
        component: AnalyticsComponentFactory,
      ),
    ],
  ),
];

Dashboard template with child outlet:

<div class="dashboard">
  <nav>
    <a [routerLink]="['/dashboard', 'overview']">Overview</a>
    <a [routerLink]="['/dashboard', 'analytics']">Analytics</a>
  </nav>
  <router-outlet [routes]="childRoutes"></router-outlet>
</div>

Programmatic Navigation

@Component(
  selector: 'login-form',
  template: '<form (ngSubmit)="login()">'
      '<input [(ngModel)]="email" name="email">'
      '<button type="submit">Login</button>'
      '</form>',
  directives: [formDirectives],
)
class LoginFormComponent {
  final Router _router;
  String email = '';

  LoginFormComponent(this._router);

  void login() async {
    // Perform login...
    await _router.navigate('/dashboard');
  }
}

Listen to route changes:

@Component(
  selector: 'app-component',
  template: '<router-outlet></router-outlet>',
)
class AppComponent implements OnInit, OnDestroy {
  final Router _router;
  StreamSubscription? _sub;

  AppComponent(this._router);

  @override
  void ngOnInit() {
    _sub = _router.onRouteActivated.listen((state) {
      print('Navigated to: ${state.path}');
      // Scroll to top on navigation
      window.scrollTo(0, 0);
    });
  }

  @override
  void ngOnDestroy() {
    _sub?.cancel();
  }
}

Wildcard Routes

Handle unknown routes:

routes = [
  RouteDefinition(path: '/', component: HomeComponentFactory),
  RouteDefinition(path: '/about', component: AboutComponentFactory),
  RouteDefinition(
    path: '/**',
    component: NotFoundComponentFactory,
  ),
];

Lazy Loading

Load components on demand to reduce initial bundle size:

routes = [
  RouteDefinition(
    path: '/admin',
    loader: () async {
      // Dynamic import
      await loadLibrary('package:my_app/admin_component.dart');
      return adminComponentFactory;
    },
  ),
];

Best Practices

  • Use path-based routes over named routes
  • Implement route guards for protected areas
  • Handle 404 routes with a wildcard
  • Use lazy loading for large feature modules
  • Scroll to top on navigation
  • Use query parameters for filterable content