Ionic 5 中 CanDeactivate 防护的导航问题

Tap*_*jee 4 routes ionic-framework angular candeactivate ionic5

在我的 Ionic 5 应用程序中,我有以下导航路径。

PageHome -> PageA ->  PageB
Run Code Online (Sandbox Code Playgroud)

我已经为 PageA 实现了 CanDeactivate 防护。

export class LeavePageGuard implements CanDeactivate<isDeactivatable>{
  canDeactivate(
    component: isDeactivatable
  ): Observable<boolean> | Promise<boolean> | boolean {
    return component.canPageLeave();
  }
}
Run Code Online (Sandbox Code Playgroud)

当用户编辑某些内容并在保存之前按后退按钮时,我会弹出一个弹出窗口来确认用户是否想要离开。

  async canPageLeave() {

    if (this.byPassNav) {
      this.byPassNav = false;
      return true;
    }
    if (JSON.stringify(this.dataOld) != JSON.stringify(this.data)) {

      const alert = await this.alertCtrl.create({
        header: 'Unsaved Chnages',
        message: 'Do you want to leave?',
        buttons: [
          {
            text: 'No',
            role: 'cancel',
            handler: () => { }
          },
          {
            text: 'Yes'),
            role: 'goBack',
            handler: () => { }
          }
        ]
      });
      await alert.present();
      let data = await alert.onDidDismiss();
      if (data.role == 'goBack') {
        return true;
      } else {
        return false;
      }
    } else {
      return true;
    }
  }
Run Code Online (Sandbox Code Playgroud)

为了继续前进,PageB我正在使用boolean byPassNav. 我在继续之前将此值设置为 TRUE 并且该方法canPageLeave正在返回TRUE

除以下情况外,前向导航在一种情况下不起作用。

on PageA change some data and click on back button -> Confirmation pop up will open -> Select No -> Confirmation pop up will close and the same page remains open. Select button to move forward to PageB.
Run Code Online (Sandbox Code Playgroud)

这会将导航移动到pageB,但也会使该页面成为根页面并删除所有路线历史记录。PageB从此流程之后我就无法再回去了。

编辑:添加代码isDeactivatable

export interface isDeactivatable {
    canPageLeave: () => Observable<boolean> | Promise<boolean> | boolean;
}
Run Code Online (Sandbox Code Playgroud)

seb*_*ras 5

似乎您只想canDeactivate在向后导航时执行守卫,而不是在向前导航时执行。

如果是这种情况,请看一下这个有效的 Stackblitz 演示

演示

您可以避免使用byPassNav(这样您就不需要手动更新其值)并通过以下方式稍微更新您的防护:

import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot } from "@angular/router";
import { Observable } from "rxjs";

export interface isDeactivatable {
  canPageLeave: (
    nextUrl?: string // <--- here!
  ) => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable()
export class CanLeavePageGuard implements CanDeactivate<isDeactivatable> {
  canDeactivate(
    component: isDeactivatable,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    return component.canPageLeave(nextState.url); // <--- and here!
  }
}

Run Code Online (Sandbox Code Playgroud)

请注意,唯一的变化是该canLeave()方法现在将获取用户尝试导航到的下一页的 URL。

通过这个小更改,您可以使用下一页的 url 来决定用户是否应该看到警报提示:

async canPageLeave(nextUrl?: string) {
    if (this.status === "saved") {
      return true;
    }

    if (nextUrl && !nextUrl.includes("home")) {
      return true;
    }

    const alert = await this.alertCtrl.create({
      header: "Unsaved Chnages",
      message: "Do you want to leave?",
      buttons: [
        {
          text: "No",
          role: "cancel",
          handler: () => {}
        },
        {
          text: "Yes",
          role: "goBack",
          handler: () => {}
        }
      ]
    });

    await alert.present();

    const data = await alert.onDidDismiss();

    if (data.role == "goBack") {
      return true;
    } else {
      return false;
    }
  }
Run Code Online (Sandbox Code Playgroud)

还有另一种“替代”方法,涉及从NavController.

这种方法更像是一种解决方法,因为导航方向实际上是 的私有属性NavigationController,但如果我们愿意,我们仍然可以访问它:

async canPageLeave() {
    if (this.status === "saved") {
      return true;
    }   

    // ----------------------
    // Alternative approach
    // ----------------------
    // The direction is a private property from the NavController
    // but we can still use it to see if the user is going back
    // to HomePage or going forward to SecondPage.
    // ----------------------

    const { direction } = (this.navCtrl as unknown) as {
      direction: "forward" | "back" | "root";
    };

    if (direction !== "back") {
      return true;
    }

    const alert = await this.alertCtrl.create({
      header: "Unsaved Chnages",
      message: "Do you want to leave?",
      buttons: [
        {
          text: "No",
          role: "cancel",
          handler: () => {}
        },
        {
          text: "Yes",
          role: "goBack",
          handler: () => {}
        }
      ]
    });

    await alert.present();

    const data = await alert.onDidDismiss();

    if (data.role == "goBack") {
      return true;
    } else {
      return false;
    }
  }
Run Code Online (Sandbox Code Playgroud)

这种方法可能听起来更简单,因为您不需要手动检查下一个 url,但请记住,Ionic 团队将来可能会在没有任何通知的情况下删除它(因为它是私有财产),所以最好只使用与上面解释的类似nextUrl

  • 事实上,使用 NavController 解决了这个问题。但这很奇怪,因为他们说我们应该使用路由器。谢谢! (3认同)