Routing
Resolver
Master Angular Route Resolvers, covering data pre-fetching, functional resolvers, error handling, and loading state management.
1. Learning Objectives
In this lesson, you will master Angular route resolvers. By the end of this topic, you will be able to:
- Explain how resolvers pre-fetch data before a route activates.
- Write functional resolvers using the
ResolveFntype. - Access resolved data from the
ActivatedRoute.dataobservable. - Handle errors inside resolvers to redirect on failure.
- Understand the trade-offs between resolvers and component-level data fetching.
2. Overview
A route resolver is a function that runs before a route is activated. It fetches the data required by the route's component and makes it available through the ActivatedRoute.data observable. The component does not render until the resolver completes, ensuring the view always has the data it needs.
3. Why This Topic Matters
Resolvers solve important data-loading challenges:
- Preventing Empty State Flickers: Without resolvers, a component renders immediately with empty data and then flickers when the API response arrives. Resolvers ensure data is ready before the component appears, providing a smoother user experience.
- Blocking Navigation on Slow APIs: If a resolver calls a slow API endpoint, navigation appears frozen because the component does not render until the resolver completes. You must handle loading indicators or set reasonable timeouts to avoid poor user experience.
4. Real-World Analogy
Think of resolvers like **a restaurant's kitchen prep system**:
- Without a Resolver (Order at the Table): You sit down at an empty table and the waiter takes your order. You stare at an empty plate while the kitchen prepares your meal. The meal eventually arrives, but you experience a long wait with nothing in front of you.
- With a Resolver (Pre-plated at the Counter): The kitchen prepares your meal before you sit down. When you reach your table, the plate is already there, fully prepared. You never see an empty table.
- Resolver Error Handling (Kitchen is out of ingredients): If the kitchen discovers it's out of a key ingredient, the host redirects you to a different restaurant (error route) instead of seating you at an empty table.
5. Core Concepts
Angular resolvers have the following key characteristics:
- ResolveFn: A functional resolver type that takes an
ActivatedRouteSnapshotandRouterStateSnapshotand returns an Observable, Promise, or synchronous value. - Route Data Property: The resolver's return value is attached to the route's
dataobject under the key specified in the route configuration'sresolveproperty. - Navigation Blocking: The router waits for all resolvers on a route to complete before activating the component. If any resolver throws an error or the observable errors out, navigation is cancelled.
| Approach | Data Available On Render | Loading State Required | Best For |
|---|---|---|---|
| Resolver | Yes (pre-fetched) | No (navigation is blocked until data arrives) | Critical data needed before render (product details, user profile) |
| Component ngOnInit | No (fetched after render) | Yes (loading spinner needed) | Non-critical or slow-loading supplementary data |
6. Syntax & API Reference
Below is a functional resolver that fetches product data before the product detail route activates:
Attaching the Resolver to a Route
Accessing Resolved Data in the Component
7. Visual Diagram
This diagram displays the resolver lifecycle during route navigation:
8. Live Example — Full Working Code
Below is a complete example using a resolver to pre-fetch a list of users for a dashboard page:
9. Interactive Playground
Try It Yourself Challenges:
- Simulate a slow API call (add a 3-second delay using the
delayoperator) and observe how navigation is blocked until the resolver completes. - Simulate an API error and verify the resolver redirects to a not-found page.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Mismatched resolve key names | Accessing the resolved data using a different key name than the one defined in the route configuration's resolve property. |
resolve: { product: ... } / data['item'] |
resolve: { product: ... } / data['product'] |
| Not handling resolver errors | If the resolver's Observable emits an error and there is no catchError handler, navigation is cancelled silently with no feedback to the user. |
return service.getData(); |
return service.getData().pipe(catchError(() => ...)) |
11. Best Practices
- Use resolvers for critical data only: Only use resolvers for data that the component absolutely needs before rendering. For supplementary or slow-loading data, fetch it inside the component's
ngOnInitwith a loading indicator. - Always handle errors in resolvers: Use the
catchErroroperator to handle API failures gracefully. Redirect to an error page or return fallback data to prevent silent navigation cancellation. - Keep resolvers lightweight: Avoid complex business logic inside resolvers. Delegate data fetching to services and keep the resolver focused on orchestrating the call and handling errors.
- Match resolve key names exactly: Ensure the key name in the route's
resolveproperty matches exactly when accessingroute.data['keyName']in the component.
12. Browser Compatibility/Requirements
Resolvers are implemented in TypeScript and execute within the Angular Router's navigation lifecycle. They have no browser-specific requirements and work consistently across all modern browsers.
13. Interview Questions
Q1: What is a route resolver and when should you use one?
Answer: A route resolver is a function that pre-fetches data before a route's component renders. The router waits for the resolver to complete before activating the route. Use resolvers when the component requires critical data to render its initial view (such as a product detail page that needs product data).
Q2: What happens if a resolver's Observable emits an error?
Answer: If the Observable errors out and there is no catchError handler, Angular cancels the navigation silently. The component is never rendered and the user receives no feedback. To handle this gracefully, use the catchError operator to redirect to an error page or return fallback data using the EMPTY observable.
14. Debugging Exercise
The component always displays undefined for the product name. Identify the bug:
Diagnosis: The route configuration defines the resolve key as 'productData', but the component accesses data['product']. These key names must match exactly.
Fix (choose one):
15. Practice Exercises
Exercise 1: Build a blog post resolver
Create a BlogService with a getPostById(id: string) method. Write a resolver that reads the :postId parameter from the route, fetches the post data, and makes it available to the component. Add a catchError handler that redirects to a "Post Not Found" page if the API call fails.
16. Scenario-Based Challenge
The Multi-Resolver Dashboard Challenge:
You must build a dashboard page that displays both user profile data and a list of recent notifications. Write two separate resolvers (one for the profile, one for the notifications) and attach both to the same route. In the component, access both resolved datasets from the ActivatedRoute.data observable using their respective key names.
17. Quick Quiz
Q1: How does a component access data returned by a resolver?
A) Through a service injection
B) Through the ActivatedRoute.data observable
C) Through the component's constructor parameter
Answer: B — Resolved data is available through the ActivatedRoute.data observable, using the key name defined in the route configuration's resolve property.
18. Summary & Key Takeaways
- Resolvers pre-fetch data before a route activates, preventing empty-state flickers in the component view.
- The router waits for all resolvers to complete before rendering the component.
- Access resolved data using the
ActivatedRoute.dataobservable with the matching key name from the route config. - Always handle errors inside resolvers using
catchErrorto prevent silent navigation failures.
19. Cheat Sheet
| API | Purpose |
|---|---|
ResolveFn<T> |
Type for functional resolvers returning data of type T. |
resolve: { key: resolverFn } |
Route config property that maps key names to resolver functions. |
route.data.subscribe() |
Access resolved data in the component via ActivatedRoute. |