【问题标题】:Angular2: Close ng-bootstrap modal with browser back eventAngular2:使用浏览器返回事件关闭 ng-bootstrap 模式
【发布时间】:2017-07-28 02:01:30
【问题描述】:

我的 Angular2 应用程序使用ng-bootstrap modal 来详细显示一些结果图表。出于这个原因,我将模式调整为几乎全屏(仅margin: 20px 左)。这会导致一些用户使用浏览器的后退按钮,而不是页面右上角或底部的关闭按钮。

我现在正在尝试的是取消默认的浏览器返回事件并在调用事件时关闭模式。

我正在使用来自here 的一些代码作为代码库来监听浏览器事件并用一些东西对其进行扩展:

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 也一样。 也许我可以在打开模式时插入一些“假”历史堆栈?!

对于 AngularJS 1.x,它似乎确实有效:https://stackoverflow.com/a/33454993/3623608

【问题讨论】:

    标签: angular ng-bootstrap


    【解决方案1】:

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

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

    LIVE DEMO

    【讨论】:

    • 它没有解决我的问题,因为浏览器返回事件仍在执行。
    • 你能创建一个 plunker 吗?
    • 试过了,当我按下浏览器的后退按钮时,ngOnDestroy() 不会被触发
    【解决方案2】:

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

    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" 复制而来的。我添加的重要部分是在模式打开时推动新的历史状态,并在“正常”模式关闭时返回历史。当我们使用浏览器返回按钮返回时,该函数不会被调用,我们会自动返回历史记录。

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

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

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

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

    【讨论】:

    • 如果您多次执行此操作(打开/关闭模式),我担心您会破坏浏览器历史记录。
    【解决方案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) 
      }
    ];
    

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-10
        • 2018-02-10
        相关资源
        最近更新 更多