Prevent Users From Losing Unsaved Data
Join the DZone community and get the full member experience.
Join For FreeThere are many instances where a user fills some input in a form, edits that input, and then might attempt to leave the page that they're on. However, often, we'll want to secure the form in such a way that if someone navigates away or closes the browser tab, they should be prompted to confirm that they really want to leave the form with unsaved data.
Whenever these kinds of instances occur, you will see an alert appear on the top of your browser like this:
- When a user closes or refreshes the tab.
- When navigation is changed (user clicks back or forward navigation page buttons).
So, let's go ahead with the first implementation:
When a User Closes or Refreshes the Tab
We need to register the window:beforeunload
and show a message if the user has unsaved data. The beforeunload
event is fired when the window, the document, and its resources are about to be unloaded. The document is still visible, and the event is still cancelable at this point.
This event enables a web page to trigger a confirmation dialog asking the user if they really want to leave the page. If the user confirms, the browser navigates to the new page; otherwise, it cancels the navigation.
For example:@HostListener('window:beforeunload', ['$event'])
public onPageUnload($event: BeforeUnloadEvent) {
if (this.hasUnsavedData()) {
$event.returnValue = true;
}
}
2: When the User Navigates to Another Route (Navigation Changed Event):
For the implementation of navigation changed events, you need to initially create a canDeactivate
interface. It is an interface that a class can implement to be a guard, deciding if a route can be deactivated. If all guards return true, navigation will continue. If any guard returns false, navigation will be canceled. If any guard returns a UrlTree
, current navigation will be canceled and new navigation will be kicked off to the UrlTree
returned from the guard.
canDeactivate
, which comes from '@angular/router'
.
xxxxxxxxxx
import { CanDeactivate } from '@angular/router';
Then, we create an interface for the
HasUnsavedData
method and import it as well in a guard file/component.
xxxxxxxxxx
export interface HasUnsavedData {
hasUnsavedData(): boolean;
}
Add the path to the route config file.
xxxxxxxxxx
{path:'user/new',component:UserFormComponent,canDeactivate: [CanDeactivateGuard]}
Add
CanDeactivate
to the ngModuleproviders. Now, we write the main method to handle this event and showing the popups for displaying the message for unsaved data in the guard file.
xxxxxxxxxx
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { HasUnsavedData } from './core.interface';
@Injectable()
export class HasUnsavedDataGuard implements CanDeactivate {
canDeactivate(component: HasUnsavedData): boolean {
if (component.hasUnsavedData && component.hasUnsavedData()) {
return confirm('You have some unsaved form data.
Are you sure, you want to leave this page?');
}
return true;
}
}
We will have to implement the method, CanDeactivate
, which gets the component instance and returns true or false. In this example, true will be returned if the message popped up and the user confirmed. Otherwise, the route won't be changed.
Conclusion
To sum up all the blog, as a best practice we should create a generic component to handle any form components. This abstract component will be registered to the browser event and the angular guard will invoke its API: CanDeactivate
. It will alert the user that data is unsaved when the form is actually left, not submitted, and dirty.
Further Reading
Opinions expressed by DZone contributors are their own.
Comments