【问题标题】:How to subscribe asynchronously to matdialog service for candeactivate guard?如何为candeactivate Guard异步订阅matdialog服务?
【发布时间】:2020-02-28 07:39:24
【问题描述】:

我已经使用角度表单验证实现了 candeactivate 保护。 如果用户单击 ngForm 字段。并尝试导航到不同的选项卡,用户将收到一个自定义确认弹出窗口,其中会显示“放弃更改?”并返回 true 或 false。

这是我的表单保护

import { NgForm } from "@angular/forms";
import { ComponentCanDeactivate } from './component-can-deactivate';

export abstract class FormCanDeactivate extends ComponentCanDeactivate {

abstract get form(): NgForm;

canDeactivate(): boolean {
    return this.form.submitted || !this.form.dirty;
}
}

组件保护

import { HostListener } from "@angular/core";

export abstract class ComponentCanDeactivate {

abstract canDeactivate(): boolean;

@HostListener('window:beforeunload', ['$event'])
unloadNotification($event: any) {
    if (!this.canDeactivate()) {
        $event.returnValue = true;
    }
}
}

现在这是我的确认弹出代码。我的问题是,如果我使用默认的 confirm() 方法(下面代码中的注释行),它会弹出窗口,并询问是或否,这很完美。但是如果我在这里使用自定义材质弹出窗口, 我必须订阅 afterclose() 方法,该方法异步执行,而我必须等到该方法执行后再继续。我怎样才能做到这一点?

import { Injectable } from '@angular/core';
import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { MatMenuTrigger, MatDialog } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { ComponentCanDeactivate } from './component-can-deactivate';
import { ConfirmationComponent } from 'src/app/core/modals/confirmation/confirmation.component';


@Injectable()
export class CanDeactivateGuard implements CanDeactivate<ComponentCanDeactivate> {

    constructor(private modalService: MatDialog) {

    }

canDeactivate(component: ComponentCanDeactivate): boolean {

    if (!component.canDeactivate()) {
        // return confirm('You have unsaved changes! If you leave, your changes will be lost');

        const dialogRef = this.modalService.open(ConfirmationComponent, {});
        dialogRef.afterClosed().subscribe(res => {
            if (res == 'OK') {
                return true;
            } else {
                return false;
            }
        });
    }
        return true;
    }
}

从模态我返回“OK”,如下所示

constructor(private dialogRef: MatDialogRef<ConfirmationComponent>) { }

btnOk() {
   this.dialogRef.close('OK');

}

感谢任何帮助。

编辑:

我在我的组件中扩展了 formdeactivate

export class EditFormComponent extends FormCanDeactivate implements OnInit {

@ViewChild('form', { static: true }) form: NgForm;

constructor(){super();}
}

Stackblitz 链接:https://angular-custom-popup-candeactivate.stackblitz.io

【问题讨论】:

  • 你能在堆栈闪电战中重新创建吗?你正在返回一个布尔值,你应该返回某种承诺或可观察的。
  • 嗨,库尔特,我在这里提到了这个例子,但是我想使用一个自定义弹出窗口而不是确认()方法,它返回是或否。 stackblitz.com/edit/ang-form-candeactivate
  • 你在使用 Angular 5 吗?
  • 实际上是 Angular 8。
  • 你能提供一个 Angular 8 stackblitz

标签: angular angular-material observable subscribe candeactivate


【解决方案1】:

你的问题

您想要一种可重用的方式在用户离开包含脏表单的组件之前提示用户。

要求:

  • 表格是否干净无提示
  • 如果用户想退出,导航会继续
  • 如果用户不想退出,导航将被取消

您现有的解决方案

我花了一点时间了解您的解决方案后,我发现这是一种处理多个组件的优雅方式。

你的设计大概是这样的:

export abstract class ComponentCanDeactive {
  abstract canDeactivate(): boolean;
}

export abstract class FormCanDeactivate extends ComponentCanDeactivate {
  abstract get form(): NgForm;

  canDeactivate(): boolean {
    return this.form.submitted || !this.form.dirty;
  }
}

如果您想将其应用于组件,只需扩展 FormCanDeactivate 类即可。

您使用 Angular CanDeactivate 路由保护来实现它。

export class CanDeactivateGuard implements CanDeactivate<ComponentCanDeactivate> {
  canDeactivate(component: ComponentCanDeactivate): boolean {
    return component.canDeactivate();
  }
}

您将其添加到路由中的相关路由。我假设您了解所有这些工作原理,因为您提供了代码和演示。

如果你只是想在组件有脏表单时防止路由失效,你已经解决了这个问题。

使用对话框

您现在希望在用户离开脏表单之前给他们一个选择。您使用同步 javascript confirm 实现了这一点,但您想使用异步的 Angular Material 对话框。

解决方案

首先,因为你要异步使用它,你需要从你的守卫返回一个异步类型。您可以返回 PromiseObservable。 Angular Material 对话框返回一个Observable,所以我将使用它。

现在只需设置对话框并返回可观察的关闭函数。

deactivate-guard.ts

constructor(private modalService: MatDialog) {}

canDeactivate(component: ComponentCanDeactivate):  Observable<boolean> {
  // component doesn't require a dialog - return observable true
  if (component.canDeactivate()) {
    return of(true);
  }

  // set up the dialog
  const dialogRef = this.modalService.open(YesNoComponent, {
    width: '600px',
    height: '250px', 
  });

  // return the observable from the dialog  
  return dialogRef.afterClosed().pipe(
    // map the dialog result to a true/false indicating whether
    // the route can deactivate
    map(result => result === true)
  );    
}

其中YesNoComponent 是您创建的自定义对话框组件,作为对话框的包装器。

export class YesNoComponent {

  constructor(private dialogRef: MatDialogRef<YesNoComponent>  ) { }

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

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

演示:https://stackblitz.com/edit/angular-custom-popup-candeactivate-mp1ndw

【讨论】:

  • 刚刚发现问题的另一个原因是角度 ngx ui 加载程序,当我弄脏表单字段并路由到不同页面时,它正在无限加载,并且不允许我输入是/否按钮.
  • 啊。从来都不简单!
  • 我们可以将 YesNo 弹出代码放在浏览器刷新或窗口关闭上的任何想法。在上面的示例中,我们有 window:beforeunload 代码来捕获 ComponentCanDeactivate 类中的窗口重新加载事件。
  • 这是一个完全不同的问题!我认为这个答案已经足够复杂而无需涉及window:beforeunload 行为。如果您遇到困难,我会做一些研究,然后发布一个新问题。
  • 很棒的答案。你拯救了我的一天,谢谢
猜你喜欢
  • 2018-08-08
  • 2021-07-17
  • 2018-01-13
  • 1970-01-01
  • 1970-01-01
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
相关资源
最近更新 更多