14

I have three components, each have their own resolver that fetch data from a different API. To do so, these components rely on an url, that is shared between them using a service.

I want that when a change to that url happens, each components reloads itself, i.e to restart the resolvers.

I had a look at several related S.O questions, but none mention how to do so when using resolvers, cf : question 1,question 2,question 3

I can think of two ways :

The dirty way : Force the refreshing of the component using a router redirect and a blank component.

this.router.navigateByUrl('blank',true);
this.router.navigateByUrl('home/(mapps:mobil//sw:simil//sf:sales)');

But that didn't work. There is a refresh, but the resolvers aren't...resolved.

The other way : use a BehaviorSubject or a ChangeDetectorRef. But then, I am clueless as to how to achieve it, considering detecting changes within my components won't help me actually restart the resolvers.

My feeling is that this has to be done through the router somehow, because it is the one knowing about the resolvers :

const routes: Routes = [
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'home',
    component: HomeComponent,
    children : [
      {
        path: '',
        component: BlankComponent
      },
      {
        path: 'simil',
        component: SwComponent,
        outlet: 'sw',
        resolve: {
          data: SwResolve
        }
      },
      {
        path: 'sales',
        component: SalesComponent,
        outlet: 'sf',
        resolve: {
          data: SalesResolve
        }
      }
    ]
  },
  {
    path: 'blank',
    component: BlankComponent
  }
];

Any hints on how to achieved this ?

Edit : Here is the related plunkr

Edit Jun 17 : There is no need for the blank component workaround anymore. To refresh components, simply call back the route :

this.router.navigateByUrl('home/(mapps:mobil//sw:simil//sf:sales)')

  • 1
    Please, provide details and [MCVE](http://stackoverflow.com/help/mcve) on how you use resolvers. When a route is injected as `ActivatedRoute` (not `ActivatedRouteSnapshot`), `data` can be subscribed, like it is shown [here](https://angular.io/docs/ts/latest/guide/router.html#!#fetch-data-before-navigating). – Estus Flask Dec 06 '16 at 17:59
  • @estus Added a detailed plunkr. Link demonstrates the use of ActivatedRouteSnapshot, which I'm already using. – Standaa - Remember Monica Dec 07 '16 at 11:56
  • Ok, I see. And where a redirect that is supposed to refresh the resolvers should happen in this plunk? – Estus Flask Dec 07 '16 at 13:08
  • @estus Updated the plunk. It's a simplified version of what I'm doing but you should get the idea. – Standaa - Remember Monica Dec 07 '16 at 13:36
  • It is the same child route, so route component isn't updated and resolver doesn't run again. If it differs (same route but different param), route component won't be updated but resolver will run and `ActivatedRoute` `data` observable can be subscribed for that. The problem here is that a point of responsibility was wrongly chosen. It doesn't make sense to use resolvers here because they refer to shared service anyway. Just use shared service directly. RxJS subject in shared service could help to make url changes subscribable. – Estus Flask Dec 07 '16 at 14:07
  • It doesn't make sense in this example, but in my case it does. The shared service is just a way to get the url the user is visiting. The resolvers then resolve data based on what this url is, by calling different APIs. – Standaa - Remember Monica Dec 07 '16 at 14:18
  • Then it probably should be not a resolver but a service that keeps an uncompleted observable with this data. Currently there are no good ways to refresh a route (possibly will change in 2.3), so resolvers should be planned accordingly. The workaround I'm aware of is to do `router.resetConfig` with *deeply cloned* `router.config` before calling `router.navigate`. – Estus Flask Dec 07 '16 at 14:31

2 Answers2

15

It is not currently possible to refresh routes. It will probably be possible to do that with RouteReuseStrategy in Angular 2.3.

Resolvers are reevaluated when a route changes and can be observed from ActivatedRoute. They are not reevaluated if a route stays the same (no param is changed) and should be designed accordingly.

Route change is asynchronous, the workaround is to chain route changes:

this.router.navigateByUrl('blank').then(() => {
  this.router.navigateByUrl('home/(mapps:mobil//sw:simil//sf:sales)');
})
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks, it finally works ! I deserve a facepalm for not noticing the route change is async. Seems like this is the most simple solution so far, as my resolvers don't take any route param. – Standaa - Remember Monica Dec 07 '16 at 15:12
6

With angular 5 and higher its possible to rerun GuardsAndResolvers

From official documentation

By default they run only when the matrix parameters of the route change. When set to paramsOrQueryParamsChange they will also run when query params change. And when set to always, they will run every time.

{
   path: 'some path',
   runGuardsAndResolvers: 'paramsOrQueryParamsChange'
   ...
}

This should re-run guards and resolvers.

alexKhymenko
  • 5,450
  • 23
  • 40