The problem arises from a legacy scenario in web development, in a page about a specific item (say product), that has a specific url (say /products/:id
), you might have a list of "related products", clicking on which needs to reload the same route with a different id. In Angular, that does not act as expected. The ngOnInit
does not get called again, because to Angular, this component did not change.
The main subscriber you would have in such a path is the route.paramMap.subscribe()
which subscribes to changes in route. In order to update all other subscribers on the page, you need to listen to the paramMap subscriber all the time. But if you pass the returned "object" of such an observable as an input parameter to a lot of components on the page, the input does not call its setter
, because to javascript, objects are by reference, and if the reference was not overwritten, the value did not change.
<my-order-button [product]="product" >Order</my-order-button>
The first solution is to bind all input values to a local observable, that gets updated on paramMap with a clone of the object. TODO: TEST THIS
this.product$ = this.route.paramMap.pipe(
switchMap(params => this.productService.GetProduct(params.get('id'))),
tap(product => {
this.reviewsSubject.next({...product});
})
);
But if you have so many components on the page, which is what I faced, the best work around is to route away, then back again into the same route. On app.component.ts (the roote component), catch the NavigationEnd event. If the route is the specific re-routing route, navigate forward.
this.router.events
.pipe(filter(e => e instanceof NavigationEnd))
.subscribe(event => {
// find the current route
let route = this.activatedRoute.snapshot;
while (route.firstChild) {
route = route.firstChild;
}
if (route.data && route.data.reroute) {
// this is not the same as redirectTo: this forces the page to reload
// do not build this in history, replace instead
this.router.navigateByUrl(route.data.reroute + route.paramMap.get('slug'), {replaceUrl: true});
}
});