【问题标题】:Navigation issue with CanDeactivate guard in Ionic 5Ionic 5 中 CanDeactivate 防护的导航问题
【发布时间】:2021-01-23 23:05:01
【问题描述】:

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

PageHome -> PageA ->  PageB

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

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

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

  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;
    }
  }

要前进到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.

这会将导航移动到pageB,但也会将该页面设为根页面并删除所有路由历史记录。在此流程之后,我无法从 PageB 返回。

编辑:添加isDeactivatable的代码

export interface isDeactivatable {
    canPageLeave: () => Observable<boolean> | Promise<boolean> | boolean;
}

【问题讨论】:

  • 你能提供一个stackblitz最小表示你的问题吗?
  • 我将此值设置为 TRUE。但您将其设置为 false,这是一个错字吗?

标签: angular ionic-framework routes ionic5 candeactivate


【解决方案1】:

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

如果是这样,请查看this working Stackblitz demo

你可以避免使用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!
  }
}

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

通过这个小改动,您可以使用下一页的 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;
    }
  }

还有另一种“替代”方法,涉及从 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;
    }
  }

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

【讨论】:

  • 嗨@sebaferreras,首先感谢您付出了这么多努力。我尝试了您的解决方案,单行更改使其工作。这是使用 NavController 和 navigateForward 方法。在我的导航中,我正在使用角度路由器并试图避免使用 NavController。所以我试着把'this.router.navigateByUrl("/second");'在您的代码中代替 'this.navCtrl.navigateForward("/second");'结果和我的一样。第三页没有后退按钮。我不知道为什么会这样,但如果我不能让它与路由器一起工作,我必须使用 NavController。
  • 不建议直接在 Ionic 应用程序中使用路由器,特别是因为 NavController 是建立在路由器之上的抽象,用于处理页面转换和其他一些事情(将页面设置为 root例如在 Angular 中没有任何意义,但在 Ionic 中确实如此)。
  • 事实上,如果你看看 herehere 你会发现NavController实际上使用了@987654337 @幕后。但是如果真的需要直接使用路由器,可以手动设置方向:this.navController.setDirection('forward'); this.router.navigateByUrl('...');(方向可以是'forward''back'或者'root'
  • 拯救了我的一天! tks
  • 确实,使用 NavController 解决了这个问题。但这很奇怪,因为他们说我们应该使用路由器。谢谢!
【解决方案2】:

"一个类可以实现的接口,作为一个守卫,决定是否可以停用路由。如果所有守卫返回true,则继续导航。如果任何守卫返回false,则取消导航。如果任何守卫返回UrlTree , 当前导航被取消,新的导航开始到从守卫返回的 UrlTree。"[src].

在这种Guards中,没有随机行为,这完全取决于您的Guards返回了什么!如果你的一个守卫返回一个 UrlTree,这个将覆盖旧的!我认为这是你的情况!

isDeactivatable 是一个组件吗?可以请添加完整的代码!您要设置防护的组件,...

【讨论】:

  • 嗨@Nadhir,抱歉回复晚了。 isDeactivatable 是我在教程中检查过的声明的接口。此外,当用户选择“否”以留在页面上时,我不会返回任何 UrlTree 并且仅在 PageA 上返回错误值。根据上面的解释,当 PageA 返回 false 值时,导航将被取消。这工作正常,因为同一个页面说打开,但向前移动到另一个 PageB 使其成为根页面。有没有办法可以在 PageA 上返回 UrlTree 以使导航堆栈保持不变?
猜你喜欢
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-26
  • 1970-01-01
  • 1970-01-01
  • 2019-07-10
相关资源
最近更新 更多