When I first started with Angular, I remember running into situations where I needed to fetch some data before my components could load properly. You might have faced the same situation, wanting the data ready before a page shows up to avoid awkward empty screens or loading spinners.
A Real-World Scenario
With resolve, you can tell Angular to "pause" navigating to that profile page until it fetches the user data you need. This way, the profile component gets fully loaded with the data right from the start.
How I Implemented Router Resolve in My Angular App
Step 1: Create the Resolver Service
I created a service that implements Angular's Resolve interface. This interface requires a resolve method that fetches the data. The method receives two parameters: the current route and the router state, letting me grab any route params like an ID.
// user-detail.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { UserService } from './user.service';
@Injectable({ providedIn: 'root' })
export class UserDetailResolver implements Resolve<any> {
constructor(private userService: UserService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
const userId = route.paramMap.get('id');
// Simulate HTTP call delay using 'of' and 'delay'
return this.userService.getUserDetails(userId).pipe(delay(1500));
}
}
Here, getUserDetails is your usual API call. The delay simulates a network wait, so you can see resolve in action.
Step 2: Add Resolver to Route Configuration
Next, I configured my Angular routes to use this resolver on the user detail route. Notice the resolve property is an object where keys represent the data name you'll access later, and values are the resolver services.
// app-routing.module.ts
const routes = [
{ path: '', component: HomeComponent },
{ path: 'users', component: UserListComponent },
{
path: 'user/:id',
component: UserDetailComponent,
resolve: { userData: UserDetailResolver }
}
];
Step 3: Access Resolved Data in the Component
// user-detail.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
template: `
<div *ngIf="user">
<h2>{{ user.name }}'s Profile Info</h2>
<p>User Email: {{ user.email }}</p>
</div>
`
})
export class UserDetailComponent implements OnInit {
user: any;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.user = this.route.snapshot.data['userData'];
}
}
Why This Works Well for Us
What I Learned Along the Way
- Resolver runs after all other guards pass, so it’s like the last gate before route activation.
- It keeps UI simple by handling async data loading outside of the component.
- You can use resolvers for multiple data sources by adding many keys in the resolve object.
- If a resolver returns null or errors, Angular cancels the route navigation — a great way to handle failed API calls gracefully.
