8

In Angular2 we can use Resolve as follows:

import { UserResolver } from './resolvers/user.resolver.ts';

export const routes: RouterConfig = [
  {
    path: 'users/:id',
    component: UserComponent,
    resolve: {
      transaction: UserResolver
    }
  }
];

But how can I best apply a resolver to run on, for example, my app.component.ts? Perhaps when the website loads for the first time, no matter what page is first loaded, I'll always need to get the user data first (for example). So how can I have this resolver apply lower down the hierarchy of routes?

JJJ
  • 32,902
  • 20
  • 89
  • 102
joshcomley
  • 28,099
  • 24
  • 107
  • 147
  • Perhaps you want something like http://stackoverflow.com/questions/37611549/how-to-pass-parameters-rendered-from-backend-to-angular2-bootstrap-method As AngularFrance said, a resolver can only be used for components added by the router, which the route component isn't. – Günter Zöchbauer Feb 24 '17 at 09:17
  • Maybe you can use APP_INITIALIZER https://hackernoon.com/hook-into-angular-initialization-process-add41a6b7e – Michael Sep 04 '17 at 06:35

1 Answers1

3

I've been wondering about the same thing.

Turns out that if the initial request to the site is for a path which has one of its components guarded by a resolve, the resolve will run before any component in the tree is instantiated. Including AppComponent.

Now the problem is that AppComponent itself is NOT a routed component and as such it cannot access the resolved data. Maybe you could inject ActivatedRoute into AppComponent and access the data via the current route's children but it seems hackish.

What I typically do is set up a resolve (or CanActivate guard) at the root of my route hierarchy to fetch that initial data. The data itself is returned by a service and wrapped inside a ReplaySubject. This guarantees that 1) the data will be shared among all subscribers; 2) the code fetching the data will only be executed once.

In other words:

  • I use an observable to fetch the async data once and share it among all the parts of my app that need it.
  • I rely on the route system to trigger the execution of that observable as early as possible in the app lifecycle.

(But I'm not using the route system to fetch the data and share it.)

Update to answer the comments:

You have to introduce a dummy parent route to support the guard, for instance:

const routes: Routes = [
  {
    path: '',  // This is the "root route"
    component: PageShellComponent,
    canActivate: [AuthGuard],
    children: [
      { path: 'home', component: PageHomeComponent },
      { path: 'inbox', component: PageInboxComponent },
      { path: 'settings', component: PageSettingsComponent }
    ]
  },
];

The parent route is the "root of the my route hierarchy" and its guard AuthGuard gets executed for ALL child routes in the application. As you can see, the parent route has an empty path and PageShellComponent's template only contains <router-outlet></router-outlet>. The sole purpose of this route is to support the guard(s).

I should add it doesn't feel very idiomatic and I'm not sure this is the best way to go.

AngularChef
  • 13,797
  • 8
  • 53
  • 69
  • This seems like a working solution. However, I don't see where "at the root of my route hierarchy" is. Say I have `/home` and `/contact`, I don't have a, say, "root" route which is triggered on both `/home` and `/contact`. Could you provide an example? – Nicky Jul 07 '17 at 13:33
  • @AngularChef please show where you set up the resolver/guard "at the root of your route hierarchy". Thanks! – static-max Oct 25 '17 at 13:49
  • This is not working for me, one difference that I have is that I have no direct children but I ase the RouterModule.forRoot – Walfrat Jun 19 '19 at 09:21
  • You also don't need to include a `component` for this to work. Without a `component` this becomes like a shared group `canActivate` – Robin De Schepper Mar 13 '20 at 13:03