mlangwell
mlangwell

Reputation: 345

How to Make a Generic Resolver in Angular (2+)

I am looking to create a super generic API Resolver in my application. I want all "GET" requests, possibly extend it to other verbs in the future, to use this resolver. I want to be able to pass the URL and the verb of the request to the resolver and then handle making the function call from there.

This resolver will be used on any Angular route definition with a param called "id" and I want to be able to specify the return type for this resolver.

This is conceptually what I am looking to do, but obviously it does not work due to implementing the interface through Angular.

export class ApiResolve<T> implements Resolve<T> {
  constructor(private _httpClient: HttpClient) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot, requestUrl: string, requestVerb: 'get'): Observable<T> {
    return this._httpClient.request<T>(requestVerb, `${requestUrl}/${route.data['id']}`);
  }
}

{ path: `user/:id`, component: UserComponent, resolve: { user: ApiResolver<User>('api.com/users', 'get') } }

Upvotes: 2

Views: 1278

Answers (2)

POP3
POP3

Reputation: 81

I had a similar but not identical usecase where I wanted to have a generic, default page resolver that gets the needed data for different components according to the URL used in the frontend that corresponds to the URL of the backend to get the data (using a HATEOAS approach). As the code provided above did not run without errors, I created a modified version that works:

// A generic resolver that fetches data based on the current route
export class GenericResolver<T> implements Resolve<T> {
  resolve(route: ActivatedRouteSnapshot) {
    const requestUrl = new URL(inject(SomeHelperService).getUrlFromRoute(route));
    return inject(HttpClient).get<T>(requestUrl.href);
  }
}

and then calling the generic resolver with:

export const routes: Routes = [
  {
    component: GenericComponentA,
    path: 'entities/:entityId',
    canActivate: [genericGuard],
    resolve: { dataX: new GenericResolver<GenericModelX>().resolve }
  },
  {
    component: GenericComponentB,
    path: 'entities/:entityId/items',
    canActivate: [genericGuard],
    resolve: { dataY: new GenericResolver<GenericModelY>().resolve }
  }
];

Upvotes: 0

255kb - Mockoon
255kb - Mockoon

Reputation: 6974

It seems the only viable way to transmit information to the resolver is to use the route data object:

export class ApiResolve<T> implements Resolve<T> {
  constructor(private _httpClient: HttpClient) { }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):  {
    const resolverData = route.data.resolverData;
    return this._httpClient.request<T>(resolverData.method, `${resolverData.url}/${route.data['id']}`);
  }
}

{ 
  path: `user/:id`, 
  component: UserComponent, 
  resolve: { user: ApiResolver<User> },
  data: { resolverData: {url: 'api.com/users', method: 'get'}}
}

Upvotes: 3

Related Questions