Angular2: закрыть ng-bootstrap modal с событием браузера

В моем приложении Angular2 используется ng-bootstrap modal, чтобы подробно показать некоторые диаграммы результатов. По этой причине я изменил модальный почти на весь экран (только margin: 20px влево). Это заставляет некоторых пользователей использовать кнопку возврата браузера, а не кнопку закрытия в верхнем правом или нижнем углу страницы.

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

Я использую код здесь в качестве базы кода, чтобы прослушивать событие браузера и расширил его некоторыми вещами:

import { PlatformLocation } from '@angular/common'

(...)

modalRef: NgbModalRef;

constructor(location: PlatformLocation) {

    location.onPopState(() => {

        console.log('pressed back!');

        // example for a simple check if modal is opened
        if(this.modalRef !== undefined) 
        {
            console.log('modal is opened - cancel default browser event and close modal');
            // TODO: cancel the default event

            // close modal
            this.modalRef.close();
        } 
        else 
        {
            console.log('modal is not opened - default browser event');
        }
    });
}

(...)
// some code to open the modal and store the reference to this.modalRef

Проблема в том, что я не знаю, как и могу ли я отменить предыдущее событие по умолчанию.

location.onPopState((event) => {

    event.preventDefault();

});

Это фактически не работает. То же самое для этого решения. Может быть, я могу вставить некоторый "поддельный" стек истории, когда открываем модальный?!

Для AngularJS 1.x кажется, что он работает: qaru.site/info/630868/...

Ответ 1

Я действительно решил свою проблему, вставив некоторое "фальшивое" состояние истории, когда открываю модальный. Функция модального открытия и модального закрытия выглядит следующим образом:

modalRef: NgbModalRef;

open(content: NgbModal) {
    this.modalRef = this.modalService.open(content);

    // push new state to history
    history.pushState(null, null, 'modalOpened');

    this.modalRef.result.then((result) => {
        this.closeResult = `Closed with: ${result}`;
    }, (reason) => {
        this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
    });
}

private getDismissReason(reason: any): string {
    // go back in history if the modal is closed normal (ESC, backdrop click, cross click, close click)
    history.back();

    if (reason === ModalDismissReasons.ESC) {
        return 'by pressing ESC';
    } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
        return 'by clicking on a backdrop';
    } else {
        return  `with: ${reason}`;
    }
}

Функции open() и getDismissReason() копируются из https://ng-bootstrap.github.io/#/components/modal "Модальные параметры по умолчанию". Важные части, которые я добавил, подталкивают новое состояние истории к модальному открытию и возвращаются в историю при "нормальном" модальном закрытии. Когда мы вернемся с помощью кнопки браузера, эта функция не вызывается, и мы автоматически возвращаемся в историю.

Чтобы убедиться, что модальный объект закрыт на событии истории, вам понадобятся следующие строки:

constructor(private location: PlatformLocation, private modalService: NgbModal)
{
    location.onPopState((event) => {
        // ensure that modal is opened
        if(this.modalRef !== undefined) {
            this.modalRef.close();
        }
});

Вывод: Когда модад открыт, мы нажимаем новое состояние истории (например, с текущей страницей). Если модально закрыто нормально (с помощью ESC, кнопки закрытия,...), событие предыстории истории запускается вручную (мы не хотим складывать состояния истории). Если событие предыстории истории запускается браузером, нам просто нужно закрыть модальное, если оно открыто. Выбранный стек истории гарантирует, что мы останемся на одной странице.

Ограничения: Добавление нового стека истории и возврат в историю также дает возможность идти вперед в истории (после закрытия модального). Это не желаемое поведение.

Ответ 2

В режиме уничтожения Lifecycle будет исправлена ​​ваша проблема.

export class AppComponent implements OnDestroy{
  @ViewChild('childModal') childModal :CommonModalComponent;
  constructor(private viewContainerRef: ViewContainerRef) {
  }
   ngOnDestroy() {
    this.childModal.hide();
  }

}

LIVE DEMO

Ответ 3

Насколько я знаю, я думаю, что ответ на точку зрения OP:

"Теперь я пытаюсь отменить событие назад для браузера по умолчанию"

... заключается в том, что вы не можете отменить событие кнопки "Назад". Насколько я знаю, это ограничение использования браузера.

Ответ 4

ng-bootstrap модальный, теперь имеет dismissAll()

https://ng-bootstrap.github.io/#/components/modal/api#NgbModal

Вы можете закрыть все открытые модалы с помощью функции защиты маршрута, когда ng-router обнаружит изменение (включая возврат в браузер)

export class NgbModalDismissAllGuard implements CanActivateChild {
  constructor(private modalService: NgbModal) {}

  canActivateChild(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    if (this.modalService.hasOpenModals()) this.modalService.dismissAll();
    return true;
  }
}

и в роутере-конфиге..

const routes: Routes = [
  { 
    path: '', 
    canActivateChild: [NgbModalDismissAllGuard], 
    loadChildren: () => import('./Custom.Module').then(m => m.CustomModule) 
  }
];