如何异步订阅matdialog服务以启用candeactivate保护?

问题描述 投票:0回答:1

我已经使用角度形式验证实现了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;
}
}

Component Guard

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()方法(下面的代码中的注释行),它将弹出窗口,并询问YES或NO,效果很好。但是如果我在这里使用“自定义材质弹出”我必须订阅afterclosed()方法,该方法异步执行,而我必须等到该方法执行后再继续。我该如何实现?

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

angular angular-material observable subscribe candeactivate
1个回答
0
投票

您的问题

您想要一种可重用的方式来提示用户,然后离开包含脏表单的组件。

要求:

  • 如果表单干净,则不会提示
  • 如果用户要退出,导航将继续
  • 如果用户不想退出,导航将被取消

您现有的解决方案

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

您的设计大约是这个:

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 dialogRef.afterClosed().pipe(
    map(result => result === true)
  );    
}

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

export class YesNoComponent {

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.