Как асинхронно подписаться на сервис matdialog, чтобы можно было отключить охрану?
Я реализовал candeactivate guard с помощью проверки угловых форм. Если пользователь нажимает на поле 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;
}
}
Компонент 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() (закомментированная строка в приведенном ниже коде), он дает всплывающее окно и запрашивает ДА или НЕТ, что отлично работает. Но если я использую здесь всплывающее окно 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;
}
}
И из модального окна я возвращаю "ОК", как показано ниже.
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();}
}
Ссылка на Stackblitz: https://angular-custom-popup-candeactivate.stackblitz.io/
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();
}
}
Вы добавляете это к соответствующим маршрутам в своей маршрутизации. Я предполагаю, что вы понимаете, как все это работает, поскольку вы предоставили для этого код и демонстрацию.
Если вы просто хотите предотвратить деактивацию маршрута, когда компонент имеет грязную форму, вы уже решили проблему.
Использование диалога
Теперь вы хотите дать пользователю выбор, прежде чем он уйдет от грязной формы. Вы реализовали это с помощью синхронного javascriptconfirm
, но вы хотите использовать диалоговое окно 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);
}
}
ДЕМО: https://stackblitz.com/edit/angular-custom-popup-candeactivate-mp1ndw