Как подписаться асинхронно на сервис Matdialog для деактивации охраны? - PullRequest
0 голосов
/ 28 февраля 2020

Я реализовал candeactivate guard используя angular проверки форм. Если пользователь нажимает на поле ngForm. и пытается перейти на другую вкладку, пользователь получает всплывающее окно с пользовательским подтверждением, которое скажет «Отменить изменения?» и возвращает истину или ложь.

Это моя защита формы

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;
    }
}
}

Теперь вот мой код для всплывающего окна подтверждения. Моя проблема здесь заключается в том, что если я использую метод default () по умолчанию (закомментированная строка в приведенном ниже коде), он выдает windows всплывающее окно и запрашивает YES или NO, что прекрасно работает. Но если я здесь использую Custom Material Popup, я должен подписаться на метод 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');

}

Любая помощь приветствуется.

Редактировать:

Я расширил форму деактивации в моем компоненте

export class EditFormComponent extends FormCanDeactivate implements OnInit {

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

constructor(){super();}
}

Ссылка на стек: https://angular-custom-popup-candeactivate.stackblitz.io

1 Ответ

2 голосов
/ 28 февраля 2020

Ваша проблема

Требуется многоразовый способ запроса пользователей перед переходом от компонента, содержащего грязную форму.

Требования:

  • Нет запросить, если форма чиста
  • Если пользователь хочет выйти, навигация продолжится
  • Если пользователь не хочет выйти, навигация будет отменена

Ваше существующее решение

Как только я потратил немного времени на понимание вашего решения, я вижу, что это элегантный способ работы с несколькими компонентами.

Ваш дизайн примерно такой:

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, которое является асинхронным.

Решение

Во-первых, так как вы собираетесь используйте это асинхронно, вам нужно вернуть асинхронный тип из вашей охраны. Вы можете вернуть либо Promise, либо Observable. Диалог 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);
  }
}

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

...