DEV Community

Ionut LEUSTEAN
Ionut LEUSTEAN

Posted on

Angular: Prevent navigation if user performs changes

Reason

In our regular feedback sessions an issue that frequently came up was that out users start introducing data in a form and then press a wrong button and the application navigates to another page. When they come back the form is empty and all of their work is gone.

The application works just fine from a technical point of view, but the user experience suffers.

What can we do?

If the user made any changes to the page and wants to perform a navigation he will get an alert message. If he agrees, the the navigation is done and data is destroyed. If he doesn't, then he will have a chance to save the data and then continue his work.

Angular technical solution

 

1. First of all we define an interface named ComponentCanDeactivate


export interface ComponentCanDeactivate {
  canDeactivate(): boolean;
}

Enter fullscreen mode Exit fullscreen mode

 

2. And then we implement the interface

export class MyComponent implements ComponentCanDeactivate {

//...

  canDeactivate(): boolean {
    return !shouldComponentDeactivate;
  }

//...
}

Enter fullscreen mode Exit fullscreen mode

 

3. Create an Angular guard

Use Angular canDeactivateGuard. Where MessageService generates a message based on your needs and context.

I used here the javascript confirm api, but you can use any kind of component/service.


export const formCanDeactivateGuard: CanDeactivateFn<ComponentCanDeactivate> = (
  component
) => {
  const deactivateMessage = inject(MessageService).getDeactivateMessage;

  if (!component.canDeactivate()) {
    return confirm(deactivateMessage);
  }
  return true;
};

Enter fullscreen mode Exit fullscreen mode

 

4. Use the guard in router configuration


 {
    path: 'detail/:uuid',
    component: MyComponent,
    canDeactivate: [formCanDeactivateGuard],
 },
Enter fullscreen mode Exit fullscreen mode

 

If your guard activates before the application is completely loaded, transform the interface into an abstract class and then add the following method in the class.

  @HostListener('window:beforeunload', ['$event'])
  unloadNotification($event: any) {
    if (!this.canDeactivate()) {
      $event.returnValue = false;
    }
  }
Enter fullscreen mode Exit fullscreen mode

Also you can create a default behaviour, by creating a class that implements this interface and extending that class when you need it.

ex:

export class MyDefaultComponent implements ComponentCanDeactivate {
  abstract get form(): AbstractControl;

  canDeactivate(): boolean {
    return !this.form.dirty;
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion:
We can use guards to prevent a user from performing a navigation when he has unsaved data.

Top comments (0)