【问题标题】:Angular CanDeactivate Guard Not Working Properly With MatDialogAngular CanDeactivate Guard 无法与 MatDialog 一起正常工作
【发布时间】:2021-07-17 23:57:13
【问题描述】:

我创建了一个带有 CanDeactivate 的路由守卫,它确定用户是否可以在有任何未保存的更改时离开页面,并在存在任何未保存的更改时触发 MatDialog 模式。我查看了很多指南和类似的线程,并且我的代码正在运行(大部分情况下)。见下文:

这里是路由守卫,它调用组件的confirmRouteChange函数,如果有,返回结果,Observable<boolean>

@Injectable({
    providedIn: 'root'
})
export class UnsavedChangesGuard implements CanDeactivate<DirtyComponent> {
    canDeactivate(component: DirtyComponent): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        return component?.confirmRouteChange ? component.confirmRouteChange() : true;
    }
}

这里是confirmRouteChange的实现,它只是在表单脏时打开MatDialog,并返回通知对话框何时关闭的Observable:

confirmRouteChange() {
        if (this.isDirty) {
            let dialogRef = this.matDialog.open(AlertModalComponent, {
                data: {
                    msg: 'Your changes have not been saved. If you leave this page, your changes will be lost.',
                    title: 'Changes have not been saved',
                    class: 'save-changes'
                }
            });

            return dialogRef.afterClosed();
        } else {
            return true;
        }
    }

这是我的模态保存/关闭选项的实现:

save() {
        this.dialogRef.close(false);
    }

    close() {
        this.dialogRef.close(true);
    }

所以这是我的问题:当我使用未保存的更改导航离开时,模式弹出(很好),应用程序根据模式中选择的选项正确导航(很好);但是,在模式后面,我可以看到我的应用程序的其余部分导航离开页面,除了放置路由保护的组件。因此,如果我尝试导航到主页,我可以看到所有主页和未保存更改形式的组件。就好像它开始导航,停止,然后等待用户的响应。如果我使用原生的confirm() 对话框,它可以完美运行,这意味着应用程序在等待用户响应时会有效冻结。我相信这是因为如果我记录 confirm() 返回的内容,它不会返回任何内容,直到用户选择某些内容。另一方面,dialogRef.afterClosed() 立即返回 Observable,我猜这会触发导航,然后应用停止并等待用户的响应。

我认为我对模态的使用有问题,但我不确定是什么。非常感谢任何帮助!

编辑:我认为return dialogRef.afterClosed() 语句在对话框完成打开之前执行,导致导航开始然后停止。

【问题讨论】:

    标签: javascript angular candeactivate


    【解决方案1】:

    这正是你描述它的方式。

    你的路由守卫期望立即为真或假。它无法处理 observables。

    所以我的建议是做以下事情。

    第一

    进行检查,但当组件脏时直接返回 false,以便路由保护防止重新路由。建立一个服务,它存储新路由并包含一个名为例如reroutingAllowed 的字段。这最初设置为false

    canDeactivate(component: DirtyComponent): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        return component?.isDirty();
    }
    
    confirmRouteChange() {
        // here we already take the new field into account
        if (this.isDirty && !reroutingAllowed) {
            let dialogRef = this.matDialog.open(AlertModalComponent, {
                data: {
                    msg: 'Your changes have not been saved. If you leave this page, your changes will be lost.',
                    title: 'Changes have not been saved',
                    class: 'save-changes'
                }
            });
    
            // directly return false
            return false;
        } else {
            return true;
        }
    }
    

    第二

    在您的服务中并行记住用户想要前往的新路线目标,以便在用户在模态对话框中做出决定时访问它。

    第三

    等待用户回答对话。如果他想离开当前页面,请将变量 reroutingAllowed 设置为 true 并触发 Angular 路由器再次调用新的路由目标。

    这次守卫会让用户通过,尽管表单仍然很脏。

    您只需要考虑如何设置上述服务以及如何获取和记住新的路线目标。

    【讨论】:

    • 我发现另一个thread 与我的实现类似。它对他们有用,他们就像我一样返回一个 Observable。我不确定为什么它不能 100% 正确工作。我会试试你的解决方案。谢谢!
    【解决方案2】:

    我遇到了完全相同同样的问题。我可以通过简单地将 observable 转换为 Promise 来解决它。

    代替:

    dialogRef.afterClosed()
    

    这应该可以解决问题:

    dialogRef.afterClosed().toPromise()
    

    【讨论】:

      猜你喜欢
      • 2016-07-28
      • 2016-09-27
      • 2016-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多