All skills
Skillintermediate

Angular Routing

```typescript // app.routes.ts import { Routes } from '@angular/router'; import { HomeComponent } from './home/home.component';

Claude Code Knowledge Pack7/10/2026

Overview

Angular Routing

Routes Configuration

// app.routes.ts

  {
    path: '',
    redirectTo: '/home',
    pathMatch: 'full'
  },
  {
    path: 'home',
    component: HomeComponent,
    title: 'Home'
  },
  {
    path: 'users',
    loadComponent: () => import('./users/users.component').then(m => m.UsersComponent),
    title: 'Users'
  },
  {
    path: 'users/:id',
    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),
    canActivate: [authGuard],
    resolve: { user: userResolver }
  },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
    canActivate: [authGuard, adminGuard]
  },
  {
    path: '**',
    loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent),
    title: '404 Not Found'
  }
];

// app.config.ts

  providers: [
    provideRouter(
      routes,
      withComponentInputBinding(),  // Bind route params to @Input()
      withViewTransitions(),        // Enable view transitions
      withPreloading(PreloadAllModules)
    )
  ]
};

Lazy Loading

// Feature routes
// admin/admin.routes.ts

  {
    path: '',
    loadComponent: () => import('./admin-dashboard.component').then(m => m.AdminDashboardComponent)
  },
  {
    path: 'users',
    loadComponent: () => import('./admin-users.component').then(m => m.AdminUsersComponent)
  },
  {
    path: 'settings',
    loadComponent: () => import('./admin-settings.component').then(m => m.AdminSettingsComponent)
  }
];

Functional Guards

// guards/auth.guard.ts

  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isAuthenticated()) {
    return true;
  }

  // Redirect to login with return URL
  return router.createUrlTree(['/login'], {
    queryParams: { returnUrl: state.url }
  });
};

// Admin guard

  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.hasRole('admin')) {
    return true;
  }

  return router.createUrlTree(['/unauthorized']);
};

// Can deactivate (unsaved changes)

  if (component.hasUnsavedChanges()) {
    return confirm('You have unsaved changes. Are you sure you want to leave?');
  }
  return true;
};

Resolvers

// resolvers/user.resolver.ts

  const usersService = inject(UsersService);
  const id = route.paramMap.get('id')!;

  return usersService.getById(id).pipe(
    catchError(() => of(null))
  );
};

// Component receives resolved data
@Component({
  selector: 'app-user-detail',
  standalone: true,
  template: `
    @if (user) {
      <h1>{{ user.name }}</h1>
    } @else {
      <p>User not found</p>
    }
  `
})

  user = input(null);  // Resolved data bound as input
}

Route Parameters


@Component({
  selector: 'app-product-detail',
  standalone: true
})

  private route = inject(ActivatedRoute);
  private router = inject(Router);

  // Modern approach: route params as inputs
  id = input.required<string>();

  // Legacy approach: subscribe to params
  ngOnInit() {
    this.route.paramMap.subscribe(params => {
      const id = params.get('id');
      this.loadProduct(id);
    });

    // Query params
    this.route.queryParamMap.subscribe(params => {
      const filter = params.get('filter');
      const sort = params.get('sort');
    });
  }

  // Navigate programmatically
  goToEdit() {
    this.router.navigate(['/products', this.id(), 'edit']);
  }

  // Navigate with query params
  applyFilter(filter: string) {
    this.router.navigate([], {
      relativeTo: this.route,
      queryParams: { filter },
      queryParamsHandling: 'merge'  // Preserve other params
    });
  }
}

Router Events


@Component({
  selector: 'app-root',
  standalone: true
})

  private router = inject(Router);
  loading = signal(false);

  constructor() {
    // Show loading on navigation start
    this.router.events.pipe(
      filter(event => event instanceof NavigationStart)
    ).subscribe(() => {
      this.loading.set(true);
    });

    // Hide loading on navigation end
    this.router.events.pipe(
      filter(event => event instanceof NavigationEnd)
    ).subscribe(() => {
      this.loading.set(false);
    });

    // Handle navigation errors
    this.router.events.pipe(
      filter(event => event instanceof NavigationError)
    ).subscribe((event: NavigationError) => {
      console.error('Navigation error:', event.error);
      this.loading.set(false);
    });
  }
}

Child Routes & Outlets

// Parent route with child routes
const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    children: [
      {
        path: 'stats',
        component: StatsComponent,
        outlet: 'panel'  // Named outlet
      },
      {
        path: 'charts',
        component: ChartsComponent,
        outlet: 'panel'
      }
    ]
  }
];

// Dashboard component template
@Component({
  template: `
    <div class="dashboard">
      <div class="main">
        <router-outlet></router-outlet>  
      </div>
      <div class="panel">
        <router-outlet name="panel"></router-outlet>  
      </div>
    </div>
  `
})

// Navigate to named outlet
this.router.navigate(['/dashboard', { outlets: { panel: ['stats'] } }]);

Preloading Strategies

// Custom preloading strategy

@Injectable({ providedIn: 'root' })

  preload(route: Route, load: () => Observable<any>): Observable<any> {
    // Only preload routes with data.preload = true
    if (route.data?.['preload']) {
      const delay = route.data?.['preloadDelay'] || 0;
      return timer(delay).pipe(
        mergeMap(() => load())
      );
    }
    return of(null);
  }
}

// Route config with preload data
const routes: Routes = [
  {
    path: 'important',
    loadChildren: () => import('./important/important.routes'),
    data: { preload: true, preloadDelay: 2000 }
  }
];

// Register in app config
provideRouter(routes, withPreloading(CustomPreloadingStrategy))

Route Guards with Observables


  const dataService = inject(DataService);
  const router = inject(Router);

  return dataService.checkAccess(route.params['id']).pipe(
    map(hasAccess => {
      if (hasAccess) {
        return true;
      }
      return router.createUrlTree(['/no-access']);
    }),
    catchError(() => {
      return of(router.createUrlTree(['/error']));
    })
  );
};

Quick Reference

FeatureUsage
RoutesRoutes array in app.routes.ts
Lazy loadloadComponent(), loadChildren()
GuardsCanActivateFn, CanDeactivateFn
ResolversResolveFn
Paramsroute.paramMap, input()
Queryroute.queryParamMap
Navigaterouter.navigate(), routerLink
Eventsrouter.events
Outlets<router-outlet name="...">
PreloadwithPreloading()