Without using resolvers, you may present a view that has not completed loading content and will “jump” when the content finishes loading.

With using resolvers, however, there will be a noticeable lag from the time a user clicks a link to the time that the page changes, which makes your app feel unresponsive.

A decent middle ground is to start the “exit” transition immediately, then perform the “enter” transition when the route has resolved. Here’s how you can do that (note, you should be somewhat familiar with Angular router and resolvers, this isn’t a guide to using those)

Loading Indicator:

First, add a loading indicator, what it looks like is up to you but you want it to only show when busy. So:

<app-loading *ngIf="busy$ | async">
</app-loading>

Implementation of the busy$ observable (in app.component.ts) should look like this (be sure to inject private router: Router):

this.busy$ = this.router.events.pipe(
  filter(e => e instanceof ResolveStart || e instanceof ResolveEnd),
  map(e => e instanceof ResolveStart)
);

That will show the loading indicator whenever routes are being resolved.

Fast Transition:

That’s a decent start, as soon as a user clicks a link a loading indicator will show. But we can do better, in order to make the app feel responsive, we need the user to see the page start changing immediately. Let’s modify our router-outlet in this way:

<router-outlet *ngIf="!(busy$|async)"
               #router="outlet">
</router-outlet>

What this does, is as soon as the Resolve starts, our current route will disappear. When Resolve ends, the new one will appear. Add in some router transitions and you’ll have a nice responsive app with the benefits that resolved routes provide.

Multiple Outlets:

This may get a bit confused if you have multiple router outlets. To accommodate several router outlets, filter the Resolve events to those events only for the primary outlet.

this.primaryOutletBusy$ = this.router.events.pipe(
  filter(
    e =>
      (e instanceof ResolveStart || e instanceof ResolveEnd) &&
      e.state.root.children.length === 1
  ),
  map(e => e instanceof ResolveStart)
);
<router-outlet *ngIf="!(primaryOutletBusy$|async)"
               #router="outlet">
</router-outlet>