Ionic 5 中 CanDeactivate 守卫的导航问题
Navigation issue with CanDeactivate guard in Ionic 5
在我的 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;
}
“class 可以实现的接口,作为决定是否可以停用路由的守卫。如果所有守卫 return 为真,则导航继续。如果任何守卫 returns false,导航被取消。如果任何守卫 return 是 UrlTree,则当前导航被取消并且新导航从守卫 return 开始到 UrlTree 。” [src].
在这种 Guards 中,没有随机行为,完全取决于你的 Guards returned !如果你的守卫 return 一个 UrlTree,这个将覆盖旧的!我认为这是你的情况!
isDeactivatable
是一个组件吗?请添加完整的代码!您要设置守卫的组件,...
似乎你只想在向后导航时执行 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。
通过这个小改动,您可以使用下一页的 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
的 private 属性,但我们仍然可以访问它,如果我们想要:
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
可能会更好。
在我的 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;
}
“class 可以实现的接口,作为决定是否可以停用路由的守卫。如果所有守卫 return 为真,则导航继续。如果任何守卫 returns false,导航被取消。如果任何守卫 return 是 UrlTree,则当前导航被取消并且新导航从守卫 return 开始到 UrlTree 。” [src].
在这种 Guards 中,没有随机行为,完全取决于你的 Guards returned !如果你的守卫 return 一个 UrlTree,这个将覆盖旧的!我认为这是你的情况!
isDeactivatable
是一个组件吗?请添加完整的代码!您要设置守卫的组件,...
似乎你只想在向后导航时执行 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。
通过这个小改动,您可以使用下一页的 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
的 private 属性,但我们仍然可以访问它,如果我们想要:
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
可能会更好。