DEV Community

Cover image for Angular reusable resolvers
Ihor Filippov
Ihor Filippov

Posted on

Angular reusable resolvers

Hello!

At the moment, I am working at my new project - Page Health .

It is a SaaS platform which provides a page performance monitoring on the long run.

In this application I had a lot of cases, where I had to duplicate the code of my resolvers to make very, very similar things.

That's why I took thought about how to implement the DRY coding principle in my app.

First, I take a glance at official angular documentation .

And here I found an interesting way to implement resolvers with in-line functions.

export const myHero: Hero = {
  // ...
}

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'detail/:id',
        component: HeroComponent,
        resolve: {
          hero: 'heroResolver'
        }
      }
    ])
  ],
  providers: [
    {
      provide: 'heroResolver',
      useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => myHero
    }
  ]
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

So, I decided to create a function which will return a reusable provider.

export function createPageResolver(redirectRoute: string): Provider {
  return {
    provide: "pageResolver",
    deps: [NotificationsService, PagesApiService, Router],
    useFactory: (n: NotificationsService, p: PagesApiService, r: Router) => {
      return new PageResolver(n, p, r, redirectRoute);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's explain it step by step:

provide - it is the name of my provider.

deps - is shorten for the word - dependencies. In my case I need some services to do my stuff and Router from @angular/router to redirect user if condition in resolve function is not match.

useFactory - unlike the example in the official docs, I need it, because in my case I need to return a new instance of resolver, not a simple value.

And actually code of my resolver.

... imports here

export class PageResolver implements Resolve<PageDto> {
  constructor(
    private notificationsService: NotificationsService,
    private pagesApiService: PagesApiService,
    private router: Router,
    @Inject("redirectRoute") private redirectRoute: string,
  ) {}

  resolve(route: ActivatedRouteSnapshot): Observable<PageDto> {
    const param: string = route.params["id"];
    const pageId: number = Number(param);

    if (isNaN(pageId)) {
      this.notificationsService.error("Invalid page id.");
      this.router.navigate([this.redirectRoute]);
      return of(null);
    } else {
      return this.pagesApiService.getUserPage(pageId)
        .pipe(
          catchError((e) => {
            console.error(e);
            this.notificationsService.error("Unable to get this page.");
            this.router.navigate([this.redirectRoute]);
            return of(null);
          }),
        );
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

By the way, the code can be extended with custom error message, depending on situation, but in my case a static message is completely enough.

Also, please note, that I did not add an @Injecable() decorator for my resolver class, because, injection happens in useFactory method.

If you do not fully understand how to use it, here is an example.

... imports here

const routes: Routes = [
  {
    path: "",
    component: PageSpeedComponent,
    children: [
      ... some other routes
      {
        path: ":id",
        component: PageSpeedMetricsComponent,
        data: {
          title: "Chrome User Experience Report"
        },
        resolve: {
          page: "pageResolver",
        },
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
  providers: [
    createPageResolver("/user-experience"),
  ],
})
export class PageSpeedRoutingModule {}
Enter fullscreen mode Exit fullscreen mode

Hope, this small technick will be useful for somebody :)

Top comments (2)

Collapse
 
balajibalamurugan profile image
BalajiBalamurugan

can you share repo link ?

Collapse
 
igorfilippov3 profile image
Ihor Filippov