如何使 location.back() 触发 CanDeactivate? (在 Angular 13)
How to Make the location.back() trigger CanDeactivate? ( in Angular 13)
大约五年前我看到了一个类似的问题,
但我的问题有点不同:
我为整个应用程序组件编写了面包屑,作为我最外层布局的一部分。
在面包屑中,我用'location.back()'做了一个后退按钮:
<i
*ngIf="this.urlLevels > 1"
nz-icon
nzType="arrow-left"
nzTheme="outline"
(click)="back()"
></i>
import { Location } from '@angular/common';
constructor(private location: Location) {}
back() {
this.location.back();
}
我的应用程序路线如下:
{
path: '',
redirectTo: 'blog',
pathMatch: 'full',
},
{
path: 'login',
component: LoginComponent,
},
{
path: '',
canActivateChild: [RouteGuard],
children: [
{
path: 'blog',
component: LayoutComponent, // breadcrumb is a part of this component
children: [
{
path: '',
loadChildren: () => import('src/app/blog/blog.module').then((m) => m. BlogModule),
},
],
data: {
icon: '',
breadcrumb: 'Blog'
},
},
],
},
{
path: '**',
component: NotFoundComponent,
},
BlogModule 的路由如:
{ path: '', redirectTo: 'bloglist', pathMatch: 'full' },
{
path: 'bloglist',
component: BlogListComponent,
data: {
breadcrumb: '',
},
},
{
path: 'createblog',
component: CreateBlogComponent,
data: {
breadcrumb: 'CreateBlog',
},
canDeactivate: [BeforeLeaveGuard],
},
BeforeLeaveGuard:
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { CreateBlogComponent } from './create-blog.component';
@Injectable()
export class BeforeLeaveGuard implements CanDeactivate< CreateBlogComponent > {
canDeactivate(component: CreateBlogComponent): Observable<boolean> | boolean {
component.openDialog(); // I open a leave-confirm dialog here
return component.confirmed; // result of confirm dialog
}
}
问题是:
当我点击后退按钮时,我的 CanDeactivate 不起作用,我的页面像没有守卫一样改变了。
我在http://angular.io/api/router/Router看到了两种路由器的方法,
它们是:initialNavigation()、setUpLocationChangeListener()
看来他们可以帮我解决问题。
我尝试在我的 BlogModule 中使用它们,但它们都不起作用(没有任何反应,我的页面仍然成功更改,忽略了我的所有设置)。
有什么问题? T_T
我需要你的帮助。
这里发生了一些事情,但您尝试做的事情是可行的。
1。等待对话是一个异步操作
在此处的 CanDeactivate 代码中:
canDeactivate(component: CreateBlogComponent): Observable<boolean> | boolean {
component.openDialog(); // I open a leave-confirm dialog here
return component.confirmed; // result of confirm dialog
}
你打开了对话框,但是 Javascript(和 Typescript)中所有等待用户响应(或网络响应等)的都是异步的。 You need to use a Promise, Observable, or some other callback 等待并处理呈现给用户的对话框结果。如所写,您的代码将立即执行下一行 (return component.confirmed
),这将 return 无论 confirmed
的初始值是什么。
您还没有包含 openDialog() 的实现,因此不清楚它是什么样子,但是任何对话框组件都会 return 最终值的 Promise 或 Observable,您可以 return 直接来自 canDeactivate(如果它恰好是 Promise 或 Observable)或通过映射操作将其映射到布尔值(Promise.then() 对于 Promises,或 pipe(map() ) 对于 Observable)。例如,Angular Material mat-dialog 组件 return 是来自其 open() 方法的 Obervable,您可以 return 直接来自 canDeactivate。
2。 Angular 的浏览器历史管理
中存在错误
有关错误报告,请参阅 for a related Whosebug question and here。基本上,即使导航被 canDeactivate 拒绝,它仍然会更新浏览器历史记录并进入不良状态(重复尝试返回最终将导航您越过前一页并可能完全离开应用程序)。根据报告这是固定的,但是我发现我仍然需要使用这个“隐藏”选项来避免错误:
router.canceledNavigationResolution = 'computed';
我已经编写了一个 Stackblitz 来执行您所描述的操作,方法是在此处等待确认对话框响应(并实施路由器历史记录修复):
https://stackblitz.com/edit/angular-ivy-gaa2bh?file=src%2Fapp%2Fbefore-leave-guard.ts
大约五年前我看到了一个类似的问题, 但我的问题有点不同:
我为整个应用程序组件编写了面包屑,作为我最外层布局的一部分。
在面包屑中,我用'location.back()'做了一个后退按钮:
<i
*ngIf="this.urlLevels > 1"
nz-icon
nzType="arrow-left"
nzTheme="outline"
(click)="back()"
></i>
import { Location } from '@angular/common';
constructor(private location: Location) {}
back() {
this.location.back();
}
我的应用程序路线如下:
{
path: '',
redirectTo: 'blog',
pathMatch: 'full',
},
{
path: 'login',
component: LoginComponent,
},
{
path: '',
canActivateChild: [RouteGuard],
children: [
{
path: 'blog',
component: LayoutComponent, // breadcrumb is a part of this component
children: [
{
path: '',
loadChildren: () => import('src/app/blog/blog.module').then((m) => m. BlogModule),
},
],
data: {
icon: '',
breadcrumb: 'Blog'
},
},
],
},
{
path: '**',
component: NotFoundComponent,
},
BlogModule 的路由如:
{ path: '', redirectTo: 'bloglist', pathMatch: 'full' },
{
path: 'bloglist',
component: BlogListComponent,
data: {
breadcrumb: '',
},
},
{
path: 'createblog',
component: CreateBlogComponent,
data: {
breadcrumb: 'CreateBlog',
},
canDeactivate: [BeforeLeaveGuard],
},
BeforeLeaveGuard:
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { CreateBlogComponent } from './create-blog.component';
@Injectable()
export class BeforeLeaveGuard implements CanDeactivate< CreateBlogComponent > {
canDeactivate(component: CreateBlogComponent): Observable<boolean> | boolean {
component.openDialog(); // I open a leave-confirm dialog here
return component.confirmed; // result of confirm dialog
}
}
问题是: 当我点击后退按钮时,我的 CanDeactivate 不起作用,我的页面像没有守卫一样改变了。
我在http://angular.io/api/router/Router看到了两种路由器的方法, 它们是:initialNavigation()、setUpLocationChangeListener()
看来他们可以帮我解决问题。 我尝试在我的 BlogModule 中使用它们,但它们都不起作用(没有任何反应,我的页面仍然成功更改,忽略了我的所有设置)。
有什么问题? T_T
我需要你的帮助。
这里发生了一些事情,但您尝试做的事情是可行的。
1。等待对话是一个异步操作
在此处的 CanDeactivate 代码中:
canDeactivate(component: CreateBlogComponent): Observable<boolean> | boolean {
component.openDialog(); // I open a leave-confirm dialog here
return component.confirmed; // result of confirm dialog
}
你打开了对话框,但是 Javascript(和 Typescript)中所有等待用户响应(或网络响应等)的都是异步的。 You need to use a Promise, Observable, or some other callback 等待并处理呈现给用户的对话框结果。如所写,您的代码将立即执行下一行 (return component.confirmed
),这将 return 无论 confirmed
的初始值是什么。
您还没有包含 openDialog() 的实现,因此不清楚它是什么样子,但是任何对话框组件都会 return 最终值的 Promise 或 Observable,您可以 return 直接来自 canDeactivate(如果它恰好是 Promise
2。 Angular 的浏览器历史管理
中存在错误有关错误报告,请参阅
router.canceledNavigationResolution = 'computed';
我已经编写了一个 Stackblitz 来执行您所描述的操作,方法是在此处等待确认对话框响应(并实施路由器历史记录修复): https://stackblitz.com/edit/angular-ivy-gaa2bh?file=src%2Fapp%2Fbefore-leave-guard.ts