0

With a hierarchical route structure, I would like to get the resolvers along a particular branch to execute in parallel.

The Setup

Currently I have my routing setup like this:

app-routing.module.ts:

const routes: Routes = [
  {
    path: 'route1',
    loadChildren: () =>
      import('./route1/route1.module').then((m) => m.Route1Module),
  },
];

route1-routing.module.ts:

const routes: Routes = [
  { path: '', 
    component: Route1Component,
    resolve: {
      route1: Route1Resolver
    },
    children: [{
      path: 'subroute1',
      loadChildren: () =>
        import('../subroute1/subroute1.module').then((m) => m.Subroute1Module),
    }]
  }
];

subroute1-routing.module.ts:

const routes: Routes = [
  {
    path: '',
    component: Subroute1Component,
    resolve: { subroute1: SubRoute1Resolver },
  },
];

And then just for illustration purposes we have the two resolvers with different delays:

export class Route1Resolver implements Resolve<any> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    console.log('Route1 starting')
    return timer(6000).pipe(take(1), tap(_ => {console.log("Route1 finished")} ));
  }
}
export class SubRoute1Resolver implements Resolve<any> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    console.log('SubRoute 1 started')
    return timer(3000).pipe(take(1), tap(_ => {console.log("SubRoute1 finished")} ));
  }
}

The Output

When I visit http://localhost:4200/route1/subroute1, and I'm watching the console, every time I see that the Route1 resolver has to finish before the SubRoute1 resolver can start.

Route1 starting
Route1 finished
SubRoute 1 started
SubRoute1 finished

The Problem

The sequential execution of these resolvers introduces an undesirable delay. Ideally these two resolvers can execute in parallel, and once the last one finishes and all of the data is in, then Angular can move on to rendering what is in the <router-outlet>.

The Question

Is there a way to have these resolvers execute in parallel?

Less desirable solutions

  • Placing the Ancestor resolvers (in this case Route1 resolver) at each leaf route. While this is doable it's a bit messy and once we get into Angular Module Federation I think this idea breaks down because each microfrontend now needs to know to add in this extra resolver for something it's not concerned with.
  • Having the Route1Module somehow return something immediately, only to come back with the real data later. I think this would cause the <router-outlet> to prematurely render when not all of the data is available.
  • Having SubRoute1 resolver somehow take on the responsibility of fetching the data that Route1 resolver would fetch as well. This could work, but Route1's data is not SubRoute1's concern.
Paul
  • 31
  • 3

1 Answers1

0

I may have been thinking of this the wrong way. There are probably likely two cases here why we would want to load some kind of data asynchronously in an ancestor route:

  1. It is non-critical data required by a sub-component in the ancestor component tree (in otherwords for a component outside of the <router-outlet>). If this is the case then it could be that the responsibility of data fetching is delegated to the components, not a resolver in the routes. In this case "non-critical" means you would not interrupt the users navigation if the data was not there, to 404 for example.

  2. It is critical data. In this case the descendent resolver (SubRoute1 in the example given) would have to bare some responsibility. Though the extent of that responsibility could be abstracted away:

    1. The Route1Resolver can setup the Observable to do the data fetch, and then place it somewhere where it is accessible but will not be subscribed to by the framework. Either place it in a Service, or wrap the observable in an observable and return that so it is available through route data. Eg, return of(myDataFetchObsersable$)
    2. The SubRoute1Resolver would be responsible for retrieving the observable from Route1Resolver and integrating with it, probably with rxjs' combineLatest.
Paul
  • 31
  • 3