angular: Parameterized routes do not cause component and router links to update
When using parameterized routes, changes to the parameter are correctly recognized using the observable on the activated route, but router links that are on the component’s view are not updated. This appears to be a bug.
See a plunker with an example here.
In that example, I have a ContainerComponent
at the root which takes an id
parameter and displays a child router outlet. The child routes are available as router links on the container component:
template: `
<p>Container {{id}}</p>
<a [routerLink]="['foo']" routerLinkActive="active">foo</a>
<a [routerLink]="['bar']" routerLinkActive="active">bar</a>
<hr />
<router-outlet></router-outlet>
`,
Assuming I start at the URL /example1/foo
, I can then use the router links in the parent component to switch between id
s. For example, I can go to /example2/foo
by clicking the “Example 2” link. On that route, the ContainerComponent
correctly recognizes the change of the id
parameter and updates the view to display that id. The links in the ContainerComponent
however still link to /example1/foo
and /example1/bar
respectively. So the change of the active route to /example2/
was never recognized there.
So it seems that the relative routes do not take route changes into account. They are rendered once at the very beginning. This appears to be because the RouterLink
directive only uses the ActivatedRoute
that’s injected at construction.
The RouterLinkActive
directive works differently by listening to router events and always using the current URL.
The RouterLink
directive could obviously be changed to listen to those events too but I personally have a similar problem with my components in my application too. Because the v3 component router actively reuses components, switching between two different routes which happen to use the same components will not cause those components to go through the lifecycle events. As such, initialization that would happen in ngOnInit
only happens for the very first route.
To properly update the component, I would have to actually listen to all router events (since you can only listen to all router events) and figure out when the current component would be affected and then perform my own lifecycle—all manually.
This all feels very wrong to me, that I have to take care of so much things and listen to so many low-level events when I just want to use the router by configuration.
- Angular version: 2.0.0-rc.4
- Router version: 3.0.0-beta.2
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 22
- Comments: 28 (9 by maintainers)
@DzmitryShylovich Oh, sorry I never got back to you about this.
Implementing a custom reuse strategy seems to be what I was looking for here. That way, I can disable the router’s component reusing completely (or even use custom logic to do it selectively) and avoid the problems I have.
I’m not completely happy with it since it still leaves the underlying problem inside the default case (I am still of the opinion that this is an odd default behavior for the router), but at least we have an actual way to get around it now (unlike at 2.0 release). I’m glad that this made it into the release eventually.
The option 1 to subscribe to the router events is not really a solution in my eyes as it still moves the responsibility to reset the state to components that should not have any knowledge about their locality within the router, but I get that this made sense as a solution to the RouterLink problematic as fixed in f65ebec3edf770aadb642c0b4f4db9e49060b30a.
But yeah, I’m happy with the custom reuse strategy and this issue can be closed I guess. Thank you for your help!
For those interested or running into the situation themselves: To disable the router reusing, you basically have to implement a custom
RouteReuseStrategy
. You can use theDefaultRouteReuseStrategy
as a base and then just change theshouldReuseRoute
to returnfalse
whenever you want to disable the route reusing.Note that you cannot just return
false
permanently here as this will break the router completely. In cases wherecurr.routeConfig
andfuture.routeConfig
arenull
, you have to returntrue
instead.So basically, assuming no custom logic to actually reuse any routes, your implementation would look like this:
You can then provide that class as a
RouteReuseStrategy
using{ provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
in your NgModule and everything should work just fine.You can see this in action in this Plunker that builds upon the previously linked example.
Hi, I have the same issue ! The configuration is the same.
+1
I just ran into this also.
Ok, thx.
this is because component is reused as described here https://angular.io/docs/ts/latest/guide/router.html#!#reuse
So u have several options:
route.params
and reset component state on updates.It’s not a problem for the
ContainerComponent
since it’s actively listening to the parameter change anyway. It is already fully aware of the router presence as it gets the parameter value, so it knows that it has to handle changes and can properly handle it.However, it’s a problem further down the line. Suppose
FooComponent
in my example requires a service to be injected, so it has aprovider
set:Now, this component is completely unaware of the router. Following separation of concerns it does not need nor should know in what context it is being used. It just knows that it requires a
MyService
when its being constructed and everything is configured so that’s the case.Now, when switching between
foo
andbar
routes, this is no problem. The component gets properly created, a service instance is created and injected. However, when switching between variousfoo
routes, e.g./example1/foo
and/example2/foo
not only is theContainerComponent
being reused (as expected), but also theFooComponent
(and everything that’s inside). So theFooComponent
does not realize it now lives in a completely separate context. It does not know that the route changed, and that it’s just being used because some router decided to.Although we are in a completely different context (
example2
instead ofexample1
), theFooComponent
is not able to realize that without actively listening to router events. And even if it did, it wouldn’t be able to receive a new and cleanMyService
instance. It would be required to somehow reset the service. This would also require the service to be able to do so.So just because the router is reusing the components, we are adding a huge complexity to the application that all components and dependencies below a reused component need to be aware and capable of possible reuse as well.
@pantonis and @poke this worked for me:
app.component.ts (setting up method for scroll to top of page)
app.component.html
NOTE: Added “scroll to top” functionality because it’s largely requested and coupled with param change
app.routes.ts (setting up route parameter that will be updated)
product-item.component.ts (where param id subscription is initiated)
NOTE: There are two calls for the item object. The first one (in ngOnInit), is called as the page is constructed. The second one (in constructor) is called as param change is recognized.
@poke there is a comment from Victor on component reuse status here: https://github.com/angular/angular/issues/7757#issuecomment-236737846
Please use the add reaction feature on the initial comment instead +1 or me too