Angular2:使用浏览器后退事件关闭 ng-bootstrap 模态

Angular2: Close ng-bootstrap modal with browser back event

我的 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();

});

这实际上是行不通的。 this solution 也一样。 也许我可以在打开模式时插入一些 "fake" 历史堆栈?!

对于 AngularJS 1.x 它似乎确实有效:

On Destroy Lifecycle 钩子将解决您的问题。

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

}

LIVE DEMO

我实际上通过在打开模式时插入一些 "fake" 历史状态来解决我的问题。 模态打开和模态关闭的函数如下所示:

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 "Modal with default options" 复制的。我添加的重要部分是在模式打开时推动新的历史状态,并在 "normal" 模式关闭时返回历史。当我们使用浏览器后退按钮返回时,不会调用此函数,我们会自动返回历史记录。

要确保模式在历史回溯事件中关闭,您需要以下几行:

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

结论: 当模式打开时,我们推送一个新的历史状态(例如当前页面)。如果模态正常关闭(使用 ESC、关闭按钮……),则手动触发历史返回事件(我们不想堆叠历史状态)。如果历史回溯事件是由浏览器触发的,我们只需要在模式打开时关闭它。推送的历史堆栈确保我们保持在同一页面上。

局限性:添加新的历史堆栈并返回历史也提供了在历史中前进的机会(在关闭模式后)。这不是期望的行为。

据我所知,我认为OP的重点答案是:

"What i'm trying now is to cancel the default browser back event"

...就是不能取消后退按钮事件。据我所知,这是浏览器可用性限制。

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

您需要将这段代码添加到您的对话框组件中。

import { PlatformLocation } from '@angular/common';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
...

constructor(
    private platformLocation: PlatformLocation ,
    private modalService: NgbModal) {

    // closes modal when back button is clicked
    platformLocation.onPopState(() => this.modalService.dismissAll());
}