How I’ve Replaced Deprecated Resolvers in Angular 16

Get rid of the deprecated implementation of the Resolve<T> class-based data provider in favor of the functional ResolveFn<T>.

Kamil Konopka
Bits and Pieces

--

Photo by AltumCode on Unsplash

Whenever we wanted to get data based on our parameters/query parameters, we couldn’t consider any better solution than Resolver. It was always a useful mechanism in Angular, providing a nice separation for getting the data and actually handling it within the component.

Resolver assures data availability before navigation ends. Actually, router waits with navigation end event emission until the data is there. Then all you need to do is to subscribe to activatedRoute.data in order to use it within your component directly.

Resolver always had to implement Resolve interface which required (and still is!) resolve method, which was returning either:


Observable<MyDataType> | Promise<MyDataType> | MyDataType

If you’re using Observable pattern to provide the data, make sure your stream completes! Otherwise, you will never see your component!

Class-based resolvers have been already deprecated in the Angular v15 release, but now with version 16, when the functional approach is even more common, it makes sense to switch to the ResolveFn type definition (in case you haven’t done it yet!).

There is no difference in returned type in both approaches. Nothing has changed there. Instead of the class implementation, we will use function/function expression instead.

The benefit of using a functional approach is no need to add created resolver into the providers array!

💡 As an aside, consider using an open-source tool such as Bit to store your reusable Angular components. You can then reuse this component across multiple projects using a simple bit import your.username/yourComponent command.

Learn more:

Ok, let’s have a look at the class-based implementation:

import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Post } from '../models';
import { filter, Observable, take } from 'rxjs';
import { inject } from '@angular/core';
import { PostsFacade } from '../../store/posts';

export class PostResolver implements Resolve<Post> {
constructor(private readonly postsFacade: PostsFacade) {}

resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<Post> {
const id = route.paramMap.get('id');

return this.postsFacade.getOne(id)
.pipe(
filter<Post>((post: Post) => !!post),
take(1));
}
}

ActivatedRouteSnapshot is being used to get the value of id parameter from actual URL. Then, postFacade is being called to get post data I need, making sure, my stream will be closed after the first emitted value. Not to forget to hook it up to the route definition:

  {
path: 'blog/:id',
loadComponent: () => import('./blog/index').then(x => x.PostComponent),
resolve: { post: PostResolver },
},

Let’s compare it with the functional version:

import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
import { Post } from '../models';
import { inject } from '@angular/core';
import { PostsFacade } from '../../store/posts';
import { filter, take } from 'rxjs';

export const PostResolver: ResolveFn<Post> = (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
postsFacade: PostsFacade = inject(PostsFacade)
): Observable<Post> => postsFacade.getOne(route.paramMap.get('id'))
.pipe(
filter<Post>((post: Post) => !!post),
take(1)
);

Now, I got rid of the deprecated implementation of Resolve<T> class-based data provider in favor of functional ResolveFn<T>.

Finally, I can use resolved data within PostComponent!

import { Component, inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { map, Observable, of } from 'rxjs';
import { Post } from '../../models';
import { AsyncPipe } from '@angular/common';

@Component({
selector: 'app-post',
standalone: true,
templateUrl: './post.component.html',
styleUrls: ['./post.component.scss'],
imports: [AsyncPipe],
})
export class PostComponent implements OnInit {
post$: Observable<Post | null> = of(null);

private readonly route: ActivatedRoute = inject(ActivatedRoute);

ngOnInit(): void {
this.post$ = this.route.data.pipe(map(({ post }) => post));
}
}

Quick and simple right?

Build Angular Apps with reusable components, just like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

--

--

JavaScript/Typescript experience with biggest corporates and scalable software, with focus on reactive / performance programming approach / high code quality.