How to parse data from component into Guard in Angular

424 views Asked by At

I want to show a prompt on screen to the user if they are trying to navigate away from a page in which they have made changes to a form. I have created a Guard so far, and successfully got it to log something in the console when the user navigates away from this page. But now, I need to show a Dialog box warning them, and they should be able to cancel the 'navigation' or confirm that they wish to navigate away.

I have a method in my component containing the form that checks whether or not changes have been made to the form, now I just need to implement the rest described above.

Here is my Guard:

import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class RouterGuardGuard implements CanDeactivate<unknown> {
  canDeactivate(
    component: unknown,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    console.log('TESTING');

    return true;
  }

}

I have implemented the guard for my component inside my app-routing-module.ts as such:

  { path: 'users/edit/:id', canDeactivate: [RouterGuardGuard], component: UserEditorComponent },

So, if the user makes changes to the form inside of the UserEditorComponent I need to show a dialog box before the navigation occurs, I already have the component for this dialog box, which I use in different parts of my app.

Thanks

1

There are 1 answers

4
Richie nature On BEST ANSWER

You need your canDeactivate method to be able to confirm the state of your components form. For that, one way you can do so is to create an interface that when implemented in your component class would return the user's decision to either leave or stay in the component.

In your guard, create an interface like so

 export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

In place of type unknown use the interface you have just created:

 export class RouterGuardGuard implements CanDeactivate<CanComponentDeactivate>{...}

..and then inside your canDeactivate method return:

  canDeactivate(
    component: CanComponentDeactivate,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return component.canDeactivate();
  }

Now in your component, you need to implement the interface canComponentDeactivate that you have created. So inside the canDeactivate method that you implement from the interface, access your form and check if it is touched or dirty. If so prompt the user and return promise<boolean> or boolean. E.g

canDeactivate() {
 if (this.myForm.dirty || this.myForm.touched) {
      return new Promise((resolve) => {
        this.modalService.create({
          content:
            "Your have unsaved changes. Are you sure you want to return?",
          onOk: () => {
            resolve(true);
          },
          onCancel: () => {
            resolve(false);
          },
        });
      });
 }
}