In my current logout call, I have something like this:
getLogoutConfirmation(){
// get confirmation in modal and perform logout
// redirect to login page
}
Issue is, when logout is performed and redirect to login is called, the canDeactivate guard is called on that route, but User has already been logged out.
So how do I call canDeactivate guard before the logout is performed and cancel logout action if user doesn't want to leave.
Thanks.
In my current logout call, I have something like this:
getLogoutConfirmation(){
// get confirmation in modal and perform logout
// redirect to login page
}
Issue is, when logout is performed and redirect to login is called, the canDeactivate guard is called on that route, but User has already been logged out.
So how do I call canDeactivate guard before the logout is performed and cancel logout action if user doesn't want to leave.
Thanks.
Share Improve this question edited Jan 10, 2018 at 12:10 Roland Rácz 2,9993 gold badges19 silver badges46 bronze badges asked Jan 10, 2018 at 11:48 Gowtham Raj JGowtham Raj J 96710 silver badges26 bronze badges 2- Refer to the official Angular documentation - angular.io/api/router/CanDeactivate – Sandip Ghosh Commented Jan 10, 2018 at 11:54
-
You need a
canDeactivate
route guard on your current route. In the callbackgetLogoutConfirmation
, you can show the modal and return a promise to confirm/cancel the logout. You can also have guards on the login route but they are not relevant in your question. – Martin Parenteau Commented Jan 10, 2018 at 15:02
6 Answers
Reset to default 3Instead of logging out immediately, just navigate to your target (login?) location and add some extra paramter. Then add a "canActivate" guard to your target (login?) location, check for your extra parameter, and do the actual logout there. By this, the logout won't interfere with your canDeactivate guard and will happen (if canDeactivate says ok) at the correct time.
Change:
userLogout() {
this.authenticationService.logout();
this.router.navigate(['login']);
}
To:
userLogout() {
this.router.navigate(['login'], { queryParams: { logout: true }});
}
And in the canActivate guard do something like this:
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): ... {
if ('true' === next.queryParamMap.get('logout')){
this.authenticationService.logout();
}
You should navigate to login page based on getLogoutConfirmation()
. if you didn't navigate away then CanDeactivate
wouldn't be activated.
Otherwise, Guard canDeactivate is get invoked when navigating away from the current view or route within your angular app. You could check and for getLogoutConfirmation within canDeactivate then decide to continue or cancel navigation. CanDeactivate guard can be used to handle the unsaved change, check permission and clean up the ponent. This doesn't handle the external navigations such as browser refreshes or closing . You need to use @HostListener('window:beforeunload')
for these cases
Simply call CanDeactivate in your logout call, and do you logic there :
canDeactivate() {
if(confirm('Leave and logout ?') {
this.loginService.logout();
this.router.navigate('');
return true;
} else {
return false;
}
}
canDeactivate is just an implementation of an interface, you can call it as you would call any function
I had the same problem and I solved it by creating a ponent that handles the logging out functionality within the ngOnInit () and then redirect to the login page
ngOnInit() {
this.authenticationService.logout();
this.router.navigate(['/login']);
}
this will cause the route to be deactivate thus calling the canDeactivate guard and displaying the confirmation
I only have a few weeks of experience in angular. This is my workaround. I added the variable check for unsaved form in local storage.
<---------------------------------------------------------------------------------->
header.ponent.ts
logout(){
this.formDetect= JSON.parse(localStorage.getItem('formDetect'));
var res=true;
if(this.formDetect.isUnsaved){
res=confirm("are you sure you want to logout? You have an unsaved form");
}
if(res){
//If you are using it somewhere else, make sure to set isUnsaved to false then set local storage.
//this.formDetect.isUnsaved=false;
//localStorage.setItem("formDetect", JSON.stringify(this.formDetect));
this.accountService.logout();
this.router.navigateByUrl('/login');
}
}
<---------------------------------------------------------------------------------->
form.ponent.ts
export class FormComponent implements OnInit, SafeData {
formDetect:FormDetect;
@HostListener('window:beforeunload')
isDataSaved():boolean{
this.formDetect= JSON.parse(localStorage.getItem('formDetect'));
return!this.formDetect.isUnsaved;
}
onFormChange(){
this.formDetect.isUnsaved=true;
localStorage.setItem("formDetect", JSON.stringify(this.formDetect));
}
}
<---------------------------------------------------------------------------------->
export interface SafeData{
isDataSaved():boolean;
}
<---------------------------------------------------------------------------------->
export class FormDetect{
isUnsaved:boolean=false;
}
<---------------------------------------------------------------------------------->
Guard
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { SafeData } from '../_models/safeData';
@Injectable({
providedIn:"root"
})
export class UnsavedCheckGuard implements CanDeactivate<SafeData> {
canDeactivate(ponent: SafeData): boolean | Observable<boolean> {
// if there are no pending changes, just allow deactivation; else confirm first
var result= ponent.isDataSaved();
return result ?
true :
// NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
// when navigating away from your angular app, the browser will show a generic warning message
// see http://stackoverflow./a/42207299/7307355
confirm('You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
}
}
I have the same problem and solved this issue by checking the navigation response.
let say you have canDeactivate like this:
canActivate() {
return windows.confirm("you have unsaved work, Are you still want to logout?")
}
so with this function, you are asking if user want to do logout before saving the task? Now you can have this response inside your logout function. The function can be written like bellow:
userLogout() {
this.router.navigate(['login']).then( (response) = > {
//response get the response from canDeactivate
if (response) {
// if response is true, call the logout service
this.authenticationService.logout();
}
}
}
Here in the function, if the user wants to leave the unsaved task, you will get true inside your userLogout function. Put this response inside an if statement. So your logout service will not be called automatically. By doing this you can solve the issue