Angular Модуль наложения CDK: CanDeactivateGuard должен запускать настраиваемый модальный - PullRequest
2 голосов
/ 29 мая 2020

ОБНОВЛЕНИЕ:

Я провел рефакторинг на основе ответа @Blind Despair ниже. Теперь он использует модуль Overlay из 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 class:

@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
  ): RaOverlayRef<R> {
    const configs = new OverlayConfig({
      hasBackdrop: true,
      panelClass: ['modal', 'is-active'],
      backdropClass: 'modal-background',
    });

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

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

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

    return myOverlayRef;
  }

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

А вот RaOverlay class:

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

// R = Response Data Type, T = Data passed to Modal Type
export class RaOverlayRef<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 ( ) вернуть то, что мне нужно?

Ответы [ 2 ]

2 голосов
/ 29 мая 2020

Вы не можете вернуть логическое значение в этой защите, потому что открывающего модального окна недостаточно для определения результата, который он должен дать. Вы должны дождаться ввода пользователя, а это означает, что ваш охранник canDeactivate должен либо вернуть Promise, либо Observable. К сожалению, ваш текущий модальный диалог слишком прост для поддержки этого варианта использования. В основном вы хотите иметь что-то вроде .afterClosed(), которое возвращает Promise<boolean> или Observable<boolean>, тогда, когда ваш пользователь щелкает да / нет, вы должны выполнить обещание или передать значение в тему и завершить его. Тогда вы легко можете просто вернуть результат .afterClosed() от своего охранника. Но для этого вам нужно будет провести рефакторинг вашей службы и модального окна. Конечно, вы можете использовать afterClosed() в качестве метода для модального компонента, но вы не хотите открывать все его методы охраннику или тому, кто будет его использовать, поэтому хорошей практикой было бы ввести DialogRef, который выставляйте только то, что вы хотите показать. Но зачем изобретать велосипед, если эта проблема уже решена? Есть замечательный модуль angular под названием @angular/cdk, который вы можете использовать для создания очень умного модального диалога без особых усилий. Вы можете проверить эту статью .

1 голос
/ 29 мая 2020

Не могли бы вы немного реорганизовать что-то вроде ...

canDeactivate(
    component: ComponentCanDeactivate
  ): boolean | Observable<boolean> {
    const _v = component.canDeactivate();
    if (!_v) {
      this.openConfirmDialog();
    }
    return _v;
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...