Requirement: Showing a confirmation modal to the user when navigating to different component(UsersComponent) from the current component(ProductsComponent).If user prefers to leave, I have to navigate to some other component(SalesComponent) rather than UsersComponent.
To achieve the above am returning a Subject in canDeactivate() present in ProductsComponent. when user confirms to leave am emitting UrlTree(salesComponent path) in subject but its triggering canDeactivate recursively and ending up showing the modal multiple times.
Any Idea how to fix it ? or by using CanDeactivate guard how can I navigate to Other component than user intended?
app-routing.module.ts
const routes:Routes = [
{path: 'users', component: UsersComponent},
{path: 'products', component: ProductsComponent, canDeactivate:[CanExitGuard]},
{path: 'sales', component: SalesComponent}
];
can-exit.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
export interface CanComponentDeactivate {
canDeactivate: () => boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree>;
}
@Injectable({ providedIn: 'root' })
export class CanExitGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
products.component.ts, Please note I used Timeout and confirm dialog to simulate the asynchronous action.
import { Component, OnInit } from '@angular/core';
import { CanComponentDeactivate } from '../can-exit.guard';
import { Router,RouterStateSnapshot, UrlTree } from '@angular/router';
import { Subject } from 'rxjs';
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit, CanComponentDeactivate {
navigationSelection$: Subject<boolean | UrlTree> = new Subject();
constructor(private router: Router) { }
ngOnInit() {
}
canDeactivate() {
console.log('Can Deactivate triggered...');
const isConfirmed: boolean = confirm("Do you want to leave products? Ok - Takes to sales page");
//simulate asynchronous
setTimeout(()=>{
if(isConfirmed){
console.log('navigates to sales page');
const UrlTree: UrlTree = this.router.createUrlTree(['sales']);
this.navigationSelection$.next(UrlTree);
} else {
console.log('navigation Canceled');
this.navigationSelection$.next(false);
}
}, 7000);
return this.navigationSelection$;
}
}