7

Suppose I have the following routes:

[
    {
        path: 'view',
        children: [
            {

                path: ':id',
                component: CustomerViewComponent,
                resolve: {
                    customerViewData: CustomerResolve,
                    edit: ViewRouteResolve // Returns false
                },
                data: {
                    other: false
                },
                children: [
                    {
                        path: 'edit',
                        resolve: {
                            edit: EditRouteResolve // Returns true
                        },
                        data: {
                            other: true
                        }
                    },
                    {
                        path: '',
                        data: {
                            other: false
                        }
                    }
                ]
            }
        ]
    },
    { path: '', component: CustomerListComponent }
]

I want to use the CustomerViewComponent for /view/ and /view/1/edit

The problem is I am unable to catch this data change in my component. I have tried with the resolve or data and I can't catch any changes...

This code will not trigger as expected:

this.route.data.subscribe(m => {
    debugger; // Triggers only once the view loads, then never again
});

// This triggers quite often, but the data is always stale.
this.router.events.subscribe(m => {
    console.log(this.route.snapshot.data['edit']);
    console.log(this.route.snapshot.data['other']);
    debugger;
});

Could this be a bug? my only work around is to look at the event NavigationEnd and analyze the .url string property...

jsgoupil
  • 3,788
  • 3
  • 38
  • 53
  • The solution is rather simple, no need to use any events. I added an answer below where I explain why not, and how you instead can get your latest data. I hope it will help you. – Wilt Mar 21 '20 at 17:12

3 Answers3

6

When listening to router.events the subscribe callback will get a few different types of events, multiple per route change. The ActivationStart is the one that holds the route data. Something like the following helped me:

this.router.events.subscribe(event => {
  if (event instanceof ActivationStart) {
    let data = event.snapshot.data;
    this.titleService.setTitle(data['title'] || '_your_default_title_');
  }
})
Radu
  • 2,022
  • 3
  • 17
  • 28
  • That sort of works. But ActivationStart is triggered way before we know if the user actually ends up on the route in question, doesn't it? – BoomShaka Oct 15 '19 at 17:56
  • No need to use route events in a component. You can simply subscribe to `route.data` observable which will re-emit with the latest data on route changes. – Wilt Mar 21 '20 at 17:10
5

Try instead to use ActivatedRoute from @angular/router

this.activatedRoute.params.subscribe(params => {
    console.log(params['edit']);
    console.log(params['other']);
});
Daniel Kucal
  • 8,684
  • 6
  • 39
  • 64
  • 1
    I appreciate the answer. `this.route` is actually the `ActivatedRoute` already and I am not looking at the `params` but the `data`. – jsgoupil Nov 10 '16 at 18:46
  • 1
    Oh, right. Then check out this answer http://stackoverflow.com/a/38652281/3159399 – Daniel Kucal Nov 10 '16 at 19:25
  • 1
    Wow, this is such an ugly design... I have been browsing the bugs on GitHub and they all close them by design. There are multiple people reporting the same problem... This is a design issue. – jsgoupil Nov 10 '16 at 20:08
1

I will comment on the second part of your question:

This triggers quite often, but the data is always stale

In case you want to use routing events, You can listen to any of these events. But in your particular case you are only interested in NavigationEnd. You can solve that by filtering the emitted value from the events observable using a pipe and a filter operator, so that your observer will only run on NavigationEnd:

this.router.events.pipe(
  filter(event instanceof NavigationEnd),
).subscribe(event => {
  //... do something on navigation end...
});

Since you still used this.route.snapshot.data in your observer, your data will not change or in your words be "stale" (no matter the event), it will refer always to the same static data, the data when ngOnInit was first called.
Since the component is the same even after the route changes the component will not be destroyed and recreated, so ngOnInit will not be called again.

This could be solved by using the ActivatedRoute data observable route.data instead of the route.snapshot.data. You would have to subscribe or in case of your events solution you could use a mergeMap operator:

this.router.events.pipe(
  filter(event instanceof NavigationEnd),
  mergeMap(this.route.data),
).subscribe(data => {
  //...do something with your data...
});

But, since you are actually in a component you do NOT need to use any events, the data from the ActivatedRoute instance observable will automatically re-emit the latest values on successful route navigation. So I suggest you more or less go with the answer from @DanielKucal which already suggests using the active route which is the right way. Just change the params from his answer to data and you will have your latest data:

constructor(private route: ActivatedRoute) { }

ngOnInit() {
  this.route.data.subscribe(data => {
    //...Do something with your data here...
  }  
}
Wilt
  • 41,477
  • 12
  • 152
  • 203