CanDeactivate Route Guard in Angular

CanDeactivate Route Guard in Angular

In this article we are going to be talking about the CanDeactivate guard, however, this is not a beginner-based article. It requires some knowledge of other guards, we will only be focusing on the CanDeactivate guard.

The CanDeactivate guard is used whenever we want to navigate from a route before the current component is Deactivated. E.g. If we want to tell a user to save his data before leaving the page.

So let's say we have a route

{path: 'add-user', component: AddUserComponent}

And in the html (app-user.component.html) of our AddUserComponent we have

 <form>
   <input type="text" placeholder="User Input"/>
   <input type="submit" value="Add user"/>
</form>

Such that in our App whenever we want to route to another component and we have typed in a value in our input box, our app should display a message.

We will need to add a form class to monitor the status of our input field too, using FormControl in the app-user.component.ts

When we use the FormControl class on our element we can know the status of our form element, whether the value has been changed or not.

We bind a variable userName in the input field to the template using form control, then we can bind the user name to the template file, with the help of property binding

  <input type="text" placeholder="User Input" [formControl]="userName"/>
import {FormControl} from '@angular/forms'
export class AddUserComponent{
  userName: FormControl= new FormControl()
  constructor(){}

}

Now let's talk about CanDectivate guard, first we have to create the guard, I prefer to use the angular terminal to create Guards

  ng g guard guards/UnsavedChanges implements ---CanDeactivate

Always remember to register the guard in the module.ts as a service in the providers array.

When this generates we can see we have one guard with the name of UnsavedChangesGuard and it looks this way

The next step is to implement this guard on our route

{path: 'add-user', component: AddUserComponent, canDeactivate: [UnsavedChangesGuard]}

So whenever tries to go away from the route the guard (canDeactivate) will always execute

The CanDeactivate requires a property/generic which we will use as the component we want the Deactivate guard to affect.

export class UnSavedChangesGuard implements CanDeactivate<AddUserComponent>{

canDeactivate(component:AddUserComponent){
  if(component.userName.dirty){
     window.confirm('You have some unsaved changes are you sure, you want to leave')
   }
  return true;
}

The canDeactivate method will always return true or false. True means the navigation will be allowed otherwise it will not be.

In the canDeactivate() constructor we parse in the component so we can monitor the status of the form.

Then we can access the userName property which we created in the AddUserComponent.

We use a value called dirty to check if the value of a value of the variable is changed, .dirty property returns true if the value is changed and false if it is not.

If the dirty returns warn the user using the window.confirm() if we click okay it means the navigation will allow else we return true. Viola it works!

Refactoring:

However in the UnSavedChangeGuard we particularly mentioned a component which is the AddUserComponent, assuming we want to use the Guard in another component, it becomes an issue.

The logic we did here

 if(component.userName.dirty){
     window.confirm('You have some unsaved changes are you sure, you want to leave')
   }
  return true;
}

Ideally should be returned in the specific component and not in the guard, so Let's create an Interface called can ComponentLeave and then other UnSavedChangesGuard implements this interface.

export interface CanComponentLeave{
  canLeave:()=>boolean
}
export class UnSavedChangesGuard implements CanDeactivate<CanComponentLeave>{

canDeactivate(component:CanComponentLeave){
  if(component.canLeave) return component.canLeave()
}
return true;
}

Now in our AddUserComponent we can implement the interface, then make use of the canLeave method, then we implement our logic in the canLeave method.

import {FormControl} from '@angular/forms'
export class AddUserComponent implements CanComponentLeave{
  userName: FormControl= new FormControl()
  constructor(){}

   canLeave():boolean{
     if(this.userName.dirty){
       window.confirm('You have some unsaved changes are you sure, you want to leave')
     }
    return true;
  }
}

So now the logic is inside the component and all we need to do is implement the CanComponentLeave interface. I prefer this approach because the guard is reusable.

So we can only deactivate a component/leave a route only when we have made some changes to the input field.

Thank you for reading my article. Drop a comment if you found this interesting.

Thanks to Nisha Singlar for properly explaining this concept, credits to her.