Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a guard to keep a user from losing form data in Angular 6?

I'm looking to create a guard in my Angular 6 app that will keep a using from navigating away from the form within the site (not through closing the browser or refreshing) from a partially filled out form by asking them to confirm first.

How can I build my guard so that I can apply it to my routes and keep the user from accidentally navigating away from a dirty form?

I'm trying to inject the form into the guard component, observe if it is dirty or not, and if it is, apply that guard to any other route and get the user to confirm before navigating to them. This is about as close as I could get, any help would be greatly appreciated.

Here's what I have so far:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { NgForm } from "@angular/forms";
import { createMPForm } from "../components/site-settings/site-settings.component"

@Injectable({
  providedIn: 'root'
})

@Injectable()
export abstract class FormGuard implements CanActivate {
  abstract get form(): NgForm;
  formDirty(): boolean {
     return createMPForm.dirty
  }

  constructor() { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    if (this.formDirty()) {
      if (confirm("You have unsaved changes! If you leave, your changes will be lost.")) {
        return true;} 
      else {
        return false;}
    }
  }
}

In my form component I have this, so that the form object can be imported by the guard:

export class createMPForm {
   @ViewChild("createMPForm", {read: ElementRef}) form: ElementRef;
 }

and my routes look something like this:

import { FormGuard } from './services/form-guard.service';
import { SiteSettingsComponent } from './components/site-settings/site-settings.component';
import { SiteComponent } from './components/site/site.component';
import { AuthGuard } from './services/auth-guard.service';

const routes: Routes = [
  { path: 'sites', component: SitesComponent, canActivate: [AuthGuard] }, //the route I want to keep from navigating to if the form is dirty
  { path: 'site-settings/:siteHid', component: SiteSettingsComponent, canActivate: [AuthGuard] }// the route with the form
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [AuthGuard, FormGuard]
})
like image 780
LaurenAH Avatar asked Oct 15 '22 14:10

LaurenAH


1 Answers

What you're looking for is a CanDeactivate guard. This is a guard that will be called before navigating away from a route, and provided with the component that the current route has rendered.

You can implement this something like this:

interface FormComponent {
  hasUnsavedChanges: boolean
}

class FormGuard implements CanDeactivate<FormComponent> {
  canDeactivate(component: FormComponent) {
    return component.hasUnsavedChanges === false
  }
}

ref: https://angular.io/api/router/CanDeactivate

like image 53
Michael Avatar answered Oct 21 '22 01:10

Michael