typescript 40 lines · 6 steps

Prefetching route data with an Angular resolver

A functional ResolveFn loads an article by slug before its route activates, redirecting cleanly when the data is missing.

Explained by highlit
1import { inject } from '@angular/core';
2import { ResolveFn, Router, ActivatedRouteSnapshot } from '@angular/router';
3import { catchError, of, EMPTY } from 'rxjs';
4import { Article } from './models/article';
5import { ArticleService } from './services/article.service';
6 
7export const articleResolver: ResolveFn<Article> = (
8 route: ActivatedRouteSnapshot,
9) => {
10 const articleService = inject(ArticleService);
11 const router = inject(Router);
12 
13 const slug = route.paramMap.get('slug');
14 
15 if (!slug) {
16 router.navigate(['/articles']);
17 return EMPTY;
18 }
19 
20 return articleService.getBySlug(slug).pipe(
21 catchError((err) => {
22 if (err.status === 404) {
23 router.navigate(['/not-found'], { skipLocationChange: true });
24 } else {
25 router.navigate(['/articles']);
26 }
27 return EMPTY;
28 }),
29 );
30};
31 
32export const articleRoutes = [
33 {
34 path: 'articles/:slug',
35 loadComponent: () =>
36 import('./article-detail.component').then((m) => m.ArticleDetailComponent),
37 resolve: { article: articleResolver },
38 runGuardsAndResolvers: 'paramsChange',
39 },
40];
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Resolvers fetch and validate data before a component renders, so the view never has to handle a missing or loading state.
  2. 2Returning EMPTY from a resolver cancels navigation, making it the natural escape hatch when you redirect instead.
  3. 3inject() lets functional resolvers grab services without a class, keeping route logic small and tree-shakeable.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Prefetching route data with an Angular resolver — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code