Angular

Angular form unsaved changes alert before leaving page | Use of Angular canDeactivate guard

Spread the love

What is CanDeactivate Angular?

 

CanDeactivate is a TypeScript interface that needs to be partially implemented in order to create a router. This guard will be used by the route to determine if the route will be closed. Can be applied to any Angular component using the canDeactivate interface.

 

Use case

You can not leave the current page to any route path from any where without save the form if you have unsaved changes data in form on the current page.

 

At first we will create a form in a page and that page route guard by CanDeactivate method. On same page implements a CanDeactivate method that will give you prompt warning for any change in form to save.

 

Follow the below step to complete canDeactivate use case.

 

Step:1

 

Create a canDeactivate guard where CanDeactivate angular interface router method will be implement which return a Boolean value from your component. Next ComponentCanDeactivate interface will implement in your guard component.

 

import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
export interface ComponentCanDeactivate {
  canDeactivate: () => boolean | Observable;
}
@Injectable()
export class unSaveChangeGuard
  implements CanDeactivate
{
  constructor() {}
  canDeactivate(
    component: ComponentCanDeactivate
  ): boolean | Observable {
    return component.canDeactivate();
  }
}

 

Step: 2

 

Implements list of points below

 

  • Implements ComponentCanDeactivate interface
  • Create a reactive form and that form subscribe a subject to track valueChanges of the registration form
  • Use primng dialog service alert to prompt you have a value change.

 

import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Observable, Observer, of, Subject, takeUntil } from 'rxjs';
import { ComponentCanDeactivate } from './guard/un-save-change.guard';
import { HttpService } from './service/http.service';
import { ConfirmationService } from 'primeng/api';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
@Component({
  selector: 'hello',
  template: `

CanDeactivate Example

`,
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements OnInit, ComponentCanDeactivate, OnDestroy {
  registerForm!: FormGroup;
  submitted = false;
  private hasUnsavedData = false;
  private unsubscribe = new Subject();
  constructor(
    private formBuilder: FormBuilder,
    private _confirmationService: ConfirmationService,
    private _httpService: HttpService
  ) {
    this.registerForm = this.formBuilder.group({
      firstName: new FormControl('', [Validators.required])
    });
  }
  ngOnInit() {
		this.isFormValueChanges();
  }
  get formcontrol() {
		return this.registerForm.controls;
	}
  isFormValueChanges() {
    const form = this.registerForm;
    const initialValue = form.value;
    form.valueChanges.pipe(takeUntil(this.unsubscribe)).subscribe((value) => {
      this.hasUnsavedData = Object.keys(initialValue).some(
        (key) => form.value[key] != initialValue[key]
      );
    });
  }
  onSubmit() {}
  canDeactivate(): Observable | boolean {
    if (this.hasUnsavedData) {
      return new Observable((observer: Observer) => {
        this._confirmationService.confirm({
          message:
            'You have unsaved changes. Do you want to save and leave this page? ',
          accept: () => {
            this.isChanges().subscribe({
              next: (apiResult) => {
                observer.next(true);
                observer.complete();
              },
              error: (apiError) => {
                observer.next(false);
                observer.complete();
              },
            });
          },
          reject: () => {
            observer.next(false);
            observer.complete();
          },
        });
      });
    } else {
      return of(true);
    }
  }
  isChanges() {
    return new Observable((observer: Observer) => {
      this._httpService.fakeApiData().subscribe({
        next: (apiResult) => {
          console.log('fdd',apiResult)
          observer.next(true);
          observer.complete();
        },
        error: (apiError) => {
          observer.next(false);
          observer.complete();
        },
      });
    });
  }
  ngOnDestroy() {
    this.unsubscribe.next();
  }
}

 

Step: 3

 

  • Create a new Component name Welcome Component that link from Hello Component to test Route guard after change value in form.

 

@Component({
  selector: 'hello',
  template: `

CanDeactivate Example


Go to WelcomeComponent`,
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements OnInit, ComponentCanDeactivate, OnDestroy {
  registerForm!: FormGroup;

 

Step: 4

 

Final step add routing path and guard in RouterModule

 

const routes: Routes = [
  {
    path: '',
    redirectTo: 'hello',
    pathMatch: 'full'
  },
  {
    path: 'hello',
    component: HelloComponent,
    canDeactivate: [unSaveChangeGuard]
  },{
    path: 'welcome',
    component: WelcomeComponent
  }
];
@NgModule({
  imports:      [ BrowserModule, FormsModule, ReactiveFormsModule,RouterModule.forRoot(routes),HttpClientModule,ConfirmDialogModule,BrowserAnimationsModule],
  providers: [unSaveChangeGuard,HttpService,ConfirmationService],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

 

Stackblitz Live example

 



 

References

 

Thank you for read this article! Like our Facebook page to get more latest interesting articles update.

 


Spread the love

About Chandra

Technology lover. Professionally Software Developer and write some Technical Blog to help newcomer.
View all posts by Chandra →

Leave a Reply

Your email address will not be published. Required fields are marked *