angular 9, Can I route to somewhere else using canDeactivate?

406 views Asked by At

Thank you for spending time here.

So Im having little problem with canDeactivate. I made a page If I click go-back button, there will be a modal show up, And user can choose either escape that page or not. Which is works fine If I click only once. But here is the problem. Lets say I was located at [ page A -> page B -> page C ] and on page C, I put canDeactivate. So If I click Ok on Modal when I click go-back, It moves to page B well. But If I click Cancel on Modal first and click go-back again, When I click Ok, It moves to page A. Certainly Whenever I call canDeactivate, It acts like It already moved no matter what I click on modal.

So All I wanna do is no matter how many times I call canDeactivate, When I click Ok on modal, I want to move to Page B. Hope y'all have solution for me.

canDeactivate.file

export class DeactiveGuard implements CanDeactivate<GrammarComponent> {
  constructor(private router: Router) {}
  canDeactivate(component: GrammarComponent) {  
    return component.canDeactivate ? component.canDeactivate() : true 
  }
}

page C.file

  public canDeactivate() {
    if (!this.exceptCanDeactivate) {
      return from(
        this.modalService.open(this.exitFromExamMd, {
          centered: true,
          scrollable: true,
          windowClass: 'final-confirm',
        }).result,
      ).pipe(map((result) => result === 'yes'))
    } else {
      return true
    }
  }
1

There are 1 answers

3
julianobrasil On

According to Angular docs, the canDeactivate guard can return, among other things, an UrlTree, an Observable<UrlTree> and even a Promise<UrlTree>. A UrlTree is a way to navigate to wherever you want. As you're specifying the destiny, no matter where you click to go away, you'll always be redirected to the same destiny.

This is what you can do: in your guard, return an observable that, internally, will emit just when you close your dialog (in this example I supposed an asynchronous confirmation dialog API, as we have in @angular/material dialog):

@Injectable({providedIn: 'root'})
export class CanDeactivateGuard implements CanDeactivate<YourComponent> {

  constructor(private matDialog: MatDialog, private router: Router) {}

  canDeactivate(
    component: YourComponent,
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<UrlTree> {
    const observable = new Observable(observer => {
      const dialogRef = this.matDialog.open(YourConfirmationDialogComponent);
      dialogRef.afterClosed().subscribe((okButtonClicked) => {
          observer.next(this.router.parseUrl(urlString));
          observer.complete();
      });
    });

    return observable;
  }
}

In your case, because you're using NgbModal:

  constructor(private modalService: ModalManager) {}

  canDeactivate(): Observable<UrlTree> {
    const observable = new Observable(observer => {
      const dialogRef = this.modalService.open(this.exitFromExamMd, {
          centered: true,
          scrollable: true,
          windowClass: 'final-confirm',
        });
      dialogRef.onClose.subscribe(() => {
          observer.next(this.router.parseUrl(urlString));
          observer.complete();
      });
    });

    return observable;
  }