Ionic 5 中 CanDeactivate 防护的导航问题 [英] Navigation issue with CanDeactivate guard in Ionic 5

查看:134
本文介绍了Ionic 5 中 CanDeactivate 防护的导航问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

PageHome ->页面 A ->页面B

我已经为 PageA 实施了 CanDeactivate 保护.

导出类 LeavePageGuard 实现 CanDeactivate{可以停用(组件:isDeactivable): Observable|Promise<布尔值>|布尔{返回 component.canPageLeave();}}

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

 异步 canPageLeave() {如果(this.byPassNav){this.byPassNav = false;返回真;}if (JSON.stringify(this.dataOld) != JSON.stringify(this.data)) {const alert = await this.alertCtrl.create({header: '未保存的更改',消息:'你想离开吗?',纽扣: [{文字:'不',角色:'取消',处理程序:() =>{ }},{文本:'是'),角色:'goBack',处理程序:() =>{ }}]});等待 alert.present();让数据 = 等待 alert.onDidDismiss();if (data.role == 'goBack') {返回真;} 别的 {返回假;}} 别的 {返回真;}}

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

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

在 PageA 上更改一些数据并单击后退按钮 ->确认弹出窗口将打开 ->选择否 ->确认弹出窗口将关闭,同一页面保持打开状态.选择按钮前进到页面B.

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

isDeactivatable

添加代码

export interface isDeactivatable {canPageLeave: () =>可观察的<布尔值>|Promise<布尔值>|布尔值;}

解决方案

似乎您只想在向后导航时执行 canDeactivate 保护,而在向前导航时不执行.

如果是这样,请查看

你可以避免使用 byPassNav(这样你就不需要手动更新它的值)并通过以下方式稍微更新你的守卫:

import { Injectable } from "@angular/core";从@angular/router"导入 { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot };import { Observable } from "rxjs";导出接口 isDeactivatable {canPageLeave:(nextUrl?: string//<--- 这里!) =>可观察的<布尔值>|Promise<布尔值>|布尔值;}@Injectable()导出类 CanLeavePageGuard 实现了 CanDeactivate{可以停用(组件:isDeactivable,currentRoute: ActivatedRouteSnapshot,当前状态:路由器状态快照,nextState:路由器状态快照): Observable|Promise<布尔值>|布尔{返回 component.canPageLeave(nextState.url);//<--- 这里!}}

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

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

async canPageLeave(nextUrl?: string) {如果(this.status ===已保存"){返回真;}if (nextUrl && !nextUrl.includes(home")) {返回真;}const alert = await this.alertCtrl.create({标题:未保存的更改",消息:你要离开吗?",纽扣: [{文字:否",角色:取消",处理程序:() =>{}},{文字:是",角色:goBack",处理程序:() =>{}}]});等待 alert.present();const data = await alert.onDidDismiss();if (data.role == "goBack") {返回真;} 别的 {返回假;}}


还有另一个替代方案"涉及从 NavController 获取导航方向的方法.

这种方法更像是一种变通方法,因为导航方向实际上是 NavigationController 的一个私有属性,但我们仍然可以根据需要访问它:

异步 canPageLeave() {如果(this.status ===已保存"){返回真;}//----------------------//替代方法//----------------------//方向是来自 NavController 的私有属性//但我们仍然可以使用它来查看用户是否会返回//到主页或前进到第二页.//----------------------const { 方向 } = (this.navCtrl as unknown) as {方向:前进"|回"|根";};如果(方向!==返回"){返回真;}const alert = await this.alertCtrl.create({标题:未保存的更改",消息:你要离开吗?",纽扣: [{文字:否",角色:取消",处理程序:() =>{}},{文字:是",角色:goBack",处理程序:() =>{}}]});等待 alert.present();const data = await alert.onDidDismiss();if (data.role == "goBack") {返回真;} 别的 {返回假;}}

这种方法听起来可能更简单,因为您不需要手动检查下一个网址,但请记住,Ionic 团队将来可能会在没有任何通知的情况下将其删除(因为它是私有财产),因此可能会更好只需使用 nextUrl 如上所述.

In my Ionic 5 application I have the following navigation path.

PageHome -> PageA ->  PageB

I have implemented CanDeactivate guard for PageA.

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

When user edit something and press back button before saving I am raising a popup to confirm if user wants to leave.

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

To move forward to PageB I am using a boolean byPassNav. I am setting this value to TRUE before moving forward and the method canPageLeave is returning TRUE.

The forward navigation is not working in one scenario except the following.

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.

This will move the navigation to pageB but also will make the page as Root Page and remove all route history. I can't go back from PageB after this flow.

Edit: Adding the code for isDeactivatable

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

解决方案

Seems like you just want to execute the canDeactivate guard when navigating back, but not when navigating forward.

If that's the case, please take a look at this working Stackblitz demo:

You could avoid using the byPassNav (so that you don't need to update its value manually) and slightly update your guard in the following way:

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

Please notice that the only change is that the canLeave() method will now get the url of the next page that the user is trying to navigate to.

With that small change, you can use the url of the next page to decide if the user should see the alert prompt or not:

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


There's also another "alternative" approach that involves getting the navigation direction from the NavController.

This approach is more like a workaround because the navigation direction is actually a private property from the NavigationController, but we can still access it if we want:

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

This approach may sound simpler since you don't need to check the next url manually, but keep in mind that the Ionic team may remove it in the future without any notice (since it's a private property) so it may be better to just use the nextUrl like explained above.

这篇关于Ionic 5 中 CanDeactivate 防护的导航问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆