如何使 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