4

I have used the application in this GitHub repository as a demonstration app for my angular routing problem.

The application is working fine. Therefore, the application itself is not my focus as it is so simple, however you can access to its code completely if you want!

My problem is, that each time by navigation in this angular app, a new instance of the related component will be generated.

I made a few changes in the code to illustrate this instantiation. I added a simple counter as follow:

export class ContactListComponent implements OnInit { contacts: any[] = []; counter = 0; //<--- counter definition constructor(private contactService: ContactService) { console.log("List Component constructor: #" + ++this.counter); //<--- usage #1 } ngOnInit() { console.log("List Component ngInit: #" + ++this.counter); //<--- usage #2 this.contactService.getContacts().subscribe((data: any[]) => { this.contacts = data; }); } } 

If you look at the following figure, by each navigation that I did a new instance has been generated, therefore the counter each time will be reset and in the console it shows the ngInit and constructor has been called again and again:

enter image description here

I even tried to navigate with the following snippet, and I got the same result!:

import { Router, ActivatedRoute } from '@angular/router'; constructor( private router: Router ) { } this.router.navigate(['/contacts']); 

The question is how can I prevent this new instantiation!?

Specially, when we navigate back from second page to the first page. I am looking for a technique like a singleton that will instantiate the component only the first time that we visit a route and the other times that we visit that route, we get the same instant of the component.

Actually this app is not the main app that I am working on, my main problem is in other app that I used a subscription technique for sharing data and then when I have several instances of a component, the following code in different instances will be fired and result not as expected. Why? Because if (this.agreeTerms) will have different value in different instances at the same time!

navNext(next) { this.next = next; if (this.next === true && this.router.url.startsWith('/') && this.router.url.endsWith('/')) { this.data.unsetNext(); if (this.agreeTerms) { //<----- this.router.navigate(['/form']); } else { this.error = 'Please accept the terms and conditions'; this.scrollBottom(); } } } 
16
  • That's the way Angular works. It will create components afresh for router-outlet. Why don't you want the component to be re-created? If you were issue is component is loading data from backend and that can slow down component rendering, then, you should consider using services where data can be cached. Commented May 17, 2020 at 19:30
  • No, actually the problem is it will keep the instances and will not destroy them. For example in other JS frameworks like UI5 it will not generate a new instance each time. I think the app component seems to be a parent component and by navigation each time an instance most be created or activated is exist! Commented May 17, 2020 at 19:31
  • @MahdiJ.Ansari - swimming against the tide is never a good idea. Since this is the way the router works in Angular I'd suggest considering other possibilities like (re-)designing your application in such a way that creation of the new component instance would not be a problem. Commented May 17, 2020 at 19:39
  • 1
    try to use Location from common stackoverflow.com/questions/35446955/how-to-go-back-last-page Commented May 18, 2020 at 7:00
  • 1
    have you tried to change RouteReuseStrategy ? medium.com/@juliapassynkova/… Commented May 18, 2020 at 7:28

1 Answer 1

3

Related to the comments you were right @anatoli, using RouteReuseStrategy will solve this issue. See here for example. Firstly we have to create a service class:

RouteReuseService.ts

import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle, RouterModule, Routes, UrlSegment } from '@angular/router'; export class RouteReuseService implements RouteReuseStrategy { private handlers: { [key: string]: DetachedRouteHandle } = {}; shouldDetach(route: ActivatedRouteSnapshot): boolean { if (!route.routeConfig || route.routeConfig.loadChildren) { return false; } let shouldReuse = false; console.log('checking if this route should be re used or not’, route'); if (route.routeConfig.data) { route.routeConfig.data.reuse ? shouldReuse = true : shouldReuse = false; } return shouldReuse; } store(route: ActivatedRouteSnapshot, handler: DetachedRouteHandle): void { console.log('storing handler'); if (handler) { this.handlers[this.getUrl(route)] = handler; } } shouldAttach(route: ActivatedRouteSnapshot): boolean { console.log('checking if it should be re attached'); return !!this.handlers[this.getUrl(route)]; } retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle { if (!route.routeConfig || route.routeConfig.loadChildren) { return null; }; return this.handlers[this.getUrl(route)]; } shouldReuseRoute(future: ActivatedRouteSnapshot, current: ActivatedRouteSnapshot): boolean { let reUseUrl = false; if (future.routeConfig) { if (future.routeConfig.data) { reUseUrl = future.routeConfig.data.reuse; } } const defaultReuse = (future.routeConfig === current.routeConfig); return reUseUrl || defaultReuse; } getUrl(route: ActivatedRouteSnapshot): string { if (route.routeConfig) { const url = route.routeConfig.path; console.log('returning url', url); return url; } } } 

In the next step we have to make some changes in app.module.ts:

providers: [{provide: RouteReuseStrategy, useClass: RouteReuseService}], bootstrap: [AppComponent] 

In the last step we have to set 'reuse' to true for the component, which we'd like to reuse in the application (in app-routing.modules.ts). For example here we want to reuse the DetailComponent:

const routes: Routes = [{ path: '', component: HomeComponent }, { path: 'detail', component: DetailComponent, data: { reuse: true } }, { path: 'item', component: ItemComponent }]; 
Sign up to request clarification or add additional context in comments.

2 Comments

Error at the line #34: Type 'null' is not assignable to type 'DetachedRouteHandle'
Dont think your code actually works. When I navigate to the one that is set to reuse, it's not even loading it for the first time.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.