首页 > 解决方案 > Angular CDK 覆盖模块:CanDeactivateGuard 应该启动自定义模式

问题描述

更新:

我根据下面@Blind Despair 的回答进行了重构。它现在使用来自 Angular CDK 的覆盖模块。这是Guard的代码:

@Injectable({
  providedIn: 'root',
})
export class PendingChangesGuard
  implements CanDeactivate<ComponentCanDeactivate> {
  content = 'A simple string content modal overlay';
  constructor(private overlayService: OverlayService) {}

  canDeactivate(
    component: ComponentCanDeactivate
  ): boolean | Observable<boolean> {
    return component.canDeactivate()
      ? true
      : this.openConfirmDialog(this.content);
  }

  openConfirmDialog(content: TemplateRef<any> | ComponentType<any> | 
string) {
    const ref = this.overlayService.open(this.content, null);

    return ref.afterClosed$.subscribe((res) => {});
  }
}

问题是这一行正在返回订阅,而我的守卫必须返回布尔值或 Observable:

 return ref.afterClosed$.subscribe((res) => {});

这是OverlayService类:

@Injectable({
  providedIn: 'root',
})
export class OverlayService {
  constructor(private overlay: Overlay, private injector: Injector) {}

  open<R = any, T = any>(
    content: string | TemplateRef<any> | Type<any>,
    data: T
  ): OverlayRef<R> {
    const configs = new OverlayConfig({
      hasBackdrop: true,
      panelClass: ['modal', 'is-active'],
      backdropClass: 'modal-background',
    });

    const overlayRef = this.overlay.create(configs);

    const myOverlayRef = new OverlayRef<R, T>(overlayRef, content, 
data);

    const injector = this.createInjector(myOverlayRef, this.injector);
    overlayRef.attach(new ComponentPortal(OverlayComponent, null, 
injector));

    return myOverlayRef;
  }

  createInjector(ref: OverlayRef, inj: Injector) {
    const injectorTokens = new WeakMap([[RaOverlayRef, ref]]);
    return new PortalInjector(inj, injectorTokens);
  }
}

这是覆盖类:

export interface OverlayCloseEvent<R> {
  type: 'backdropClick' | 'close';
  data: R;
}

// R = Response Data Type, T = Data passed to Modal Type
export class OverlayRef<R = any, T = any> {
  afterClosed$ = new Subject<OverlayCloseEvent<R>>();

  constructor(
    public overlay: OverlayRef,
    public content: string | TemplateRef<any> | Type<any>,
    public data: T // pass data to modal i.e. FormData
  ) {
    overlay.backdropClick().subscribe(() => 
this._close('backdropClick', null));
  }

  close(data?: R) {
    this._close('close', data);
  }

  private _close(type: 'backdropClick' | 'close', data: R) {
    this.overlay.dispose();
    this.afterClosed$.next({
      type,
      data,
    });

    this.afterClosed$.complete();
  }
}

有关如何重构openConfirmDialog()以返回我需要的任何提示?

标签: angular

解决方案


你不能在那个守卫中返回一个布尔值,因为打开模式不足以定义它应该给出的结果。您必须等待用户输入,这意味着您的守卫canDeactivate应该返回 aPromise或 an Observable。不幸的是,您当前的模式对话框太简单,无法支持该用例。基本上,您希望获得类似.afterClosed()which 返回的内容Promise<boolean>,或者Observable<boolean>当您的用户单击是/否时,您必须解决一个承诺或向主题发出一个值并完成它。然后你可以很容易地.afterClosed()从你的后卫那里返回结果。但是您需要为此重构您的服务和模式。当然你可以拥有afterClosed()作为模态组件上的方法,但您不想将其所有方法公开给守卫或任何会使用它的方法,因此一个好的做法是引入 a DialogRef,它只会公开您想要公开的内容。但是,如果这个问题已经解决了,为什么还要重新发明轮子呢?有一个令人惊叹的 Angular 模块@angular/cdk,您可以使用它轻松地创建一个非常智能的模态对话框。你可以查看这篇文章


推荐阅读