Angular reusable resolvers
Tags: angular, webdev, codequality
Published: 2021-09-06 10:16:33

Hello!

At the moment, I am working at my new project - [Page Health](https://pagehealth.me/about) . 

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](https://angular.io/api/router/Resolve) .

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

```javascript
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 {}
```

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

```javascript
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);
    }
  }
}
```

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.

```javascript
... 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);
          }),
        );
    }
  }
}
```

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.

```javascript
... 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 {}
```

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