Issue
I am working on a navigation system for an Angular 14 app.
In app-routing.module.ts
I have:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AboutComponent } from './components/pages/about/about.component';
import { TermsComponent } from './components/pages/terms/terms.component';
const routes: Routes = [
{ path: '', component: AboutComponent },
{ path: 'about', component: AboutComponent },
{ path: 'terms', component: TermsComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
In navbar.component.ts
I have:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css'],
})
export class NavbarComponent implements OnInit {
constructor() {}
public menuItems: any[] = [
{
route: '',
text: 'Home',
},
{
route: '/about',
text: 'About',
},
{
route: '/terms',
text: 'Terms and Conditions',
},
];
ngOnInit() {}
}
In navbar.component.html
I have:
<nav>
<a href="#" class="brand">Logo</a>
<ul class="navigation">
<li *ngFor="let item of menuItems">
<a [routerLink]="[item.route]" [routerLinkActive]="'active'">
{{ item.text }}
</a>
</li>
</ul>
</nav>
There is a Stackblitz HERE with all the code.
The goal
The goal is to be able to choose which of the pages (components) should reload upon clicking its corresponding router link, even if the component is already loaded.
For example, if I want (only) the "Terms and Conditions" to always reload, I would have:
public menuItems: any[] = [
{
route: '',
text: 'Home',
reload: false
},
{
route: '/about',
text: 'About',
reload: false
},
{
route: '/terms',
text: 'Terms and Conditions',
reload: true
},
];
EDIT
Here is a solution that works but is redundant and it lacks flexibility:
In navbar.component.html
I have added a reloadComponent()
method:
public menuItems: any[] = [
{
route: '',
text: 'Home',
reload: false
},
{
route: '/about',
text: 'About',
reload: false
},
{
route: '/terms',
text: 'Terms and Conditions',
reload: true
},
];
public reloadComponent(item: any){
if (item.reload && window.location.pathname == '/terms' ) {
window.location.reload();
}
}
In navbar.component.html
I have added:
<li *ngFor="let item of menuItems">
<a [routerLink]="[item.route]" [routerLinkActive]="'active'" (click)="reloadComponent(item)">
{{ item.text }}
</a>
</li>
I consider this solution, to be honest, dumb. It's only quality its that it works, for the particular case it is needed for.
I wish there was a way that I could reload the component that corresponds to the clicked menu item and not the current page.
Questions
- How can I achieve this goal?
- Is there a flexible and "elegant" solution to this issue?
Solution
You should implement RouteReuseStrategy, which allows you to control the reusability of the components, and you can choose which components should be cached and reused when the user navigates to a route they have already visited.
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private storedRouteHandles = new Map<string, DetachedRouteHandle>();
shouldReuseRoute(current: ActivatedRouteSnapshot, next: ActivatedRouteSnapshot): boolean {
return true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.storedRouteHandles.set(route.routeConfig.path, handle);
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return this.storedRouteHandles.get(route.routeConfig.path);
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return this.storedRouteHandles.has(route.routeConfig.path);
}
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return true;
}
deleteRouteSnapshot(path: string): void {
this.storedRouteHandles.delete(path);
}
}
You need to register it into app module:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, RouteReuseStrategy } from '@angular/router';
import { AppComponent } from './app.component';
import { CustomRouteReuseStrategy } from './custom-route-reuse-strategy';
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([
// ...
])
],
declarations: [AppComponent],
providers: [
{ provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule { }
In the ActivatedRouteSnapshot you should have access to that custom route information:"reload: true":
shouldReuseRoute(current: ActivatedRouteSnapshot, next: ActivatedRouteSnapshot): boolean {
// Check if the custom parameter "myParam" exists in the route's data.
const myParam = current.data && current.data['myParam'];
// If "myParam" is set to "reuse", reuse the current route.
if (myParam === 'reuse') {
return true;
}
// Otherwise, create a new component.
return false;
}
Answered By - Zsolt Balint
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.