I have a page that has a form that checks if the user has unsaved changes before navigating away from it.
The problem is that even with a preventDefault() and return false, the user is still able to click away from the component.
Is there a way to prevent the ngOnDestroy or click event from happening?
Note: User is not going to a different route, just another tab from the same component.
ngOnDestroy() {
if (this.myForm.dirty) {
let save = confirm('You are about to leave the page with unsaved changes. Do you want to continue?');
if (!save) {
window.event.preventDefault();
return false;
}
}
}
You are mixing two concepts - navigating away means to a new route. The correct angular solution to this is implementing a CanDeactivateGuard. The docs on that are here.
A stack overflow question with answers is here.
In your situation the user is not navigating to a new page (ie. the route does not change). They are simply clicking on a new tab.
Without seeing more code, it's hard to know if both tabs are part of the same form or in two different forms.
But regardless, you need a click handler on the other tab button and in THAT click handler you need to check if the data for the current tab is unsaved (ie. if that tab is one single form, is that form dirty?).
So basically move the code you posted from ngOnDestroy
and into that click handler.
You would wanna use CanDeactivate. Here is an example.
1. Create a guard service/provider.
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
export interface CanComponentDeactivate {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
canDeactivate(component: CanComponentDeactivate) {
return component.canDeactivate ? component.canDeactivate() : true;
}
}
2. Add your guard service (CanDeactivateGuard) in your app.module providers
providers: [
CanDeactivateGuard,
]
3. Update your routing, to something like this:
{
path: "pipeline/:id",
component: OnePipelineComponent,
canDeactivate: [CanDeactivateGuard],
},
4. Implement canDeactivate
method in your component where you want to prevent ngOnDestroy. In my case, it was OnePipelineComponent
as mentioned in the route above.
canDeactivate() {
console.log('i am navigating away');
// you logic goes here, whatever that may be & must return either True or false
if (this.user.name !== this.editName) {
return window.confirm('Discard changes?');
}
return true;
}
Note: Follow step 1 & 2 only once, obviously, & just repeat step 3 & 4 for every other component where you want the same behaviour, meaning, to
prevent ngOnDestroy or in other words, to do something before a
component can be destroyed).
Check out these articles for code sample & an explanation for the code written above. CanDeactivate & CanDeactivate Guard Example