jsgoupil
jsgoupil

Reputation: 3997

Subscribe to data change in Angular 2 based on route navigation

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...

Upvotes: 7

Views: 23051

Answers (3)

Wilt
Wilt

Reputation: 44356

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...
  }  
}

Upvotes: 1

Radu
Radu

Reputation: 2060

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_');
  }
})

Upvotes: 8

Daniel Kucal
Daniel Kucal

Reputation: 9232

Try instead to use ActivatedRoute from @angular/router

this.activatedRoute.params.subscribe(params => {
    console.log(params['edit']);
    console.log(params['other']);
});

Upvotes: 5

Related Questions