简体   繁体   中英

Angular2: Close ng-bootstrap modal with browser back event

My Angular2 app uses a ng-bootstrap modal to show some result charts in detail. For this reason i resized the modal to nearly fullscreen (only margin: 20px left). This causes some users to use the browser back button instead of the close button on the top right or bottom of the page.

What i'm trying now is to cancel the default browser back event and close the modal instead when the event is called.

I'm using some code from here as code base to listen to the browser event and extended it with some stuff:

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

The problem is that i do not know how and whether i can cancel the default back event.

location.onPopState((event) => {

    event.preventDefault();

});

This actually does not work. Same for this solution . Maybe i can insert some "fake" history stack when open up the modal ?!

For AngularJS 1.x it seems actually working: https://stackoverflow.com/a/33454993/3623608

I actually solved my problem by inserting some "fake" history state when open up the modal. The function for modal opening and modal closing looks like the following:

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

The open() and getDismissReason() functions are copied from https://ng-bootstrap.github.io/#/components/modal "Modal with default options". The important parts i added are pushing the new history state on modal open and go back in history on "normal" modal close. When we go back with the browser back button this function is not called and we go back in history automatically.

To ensure that the modal is closed on history back event you need the following lines:

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

Conclusion: When the modal is opened we push a new history state (eg with the current page). If the modal is closed normally (using ESC, close button, ...) the history back event is manually triggered (we do not want to stack history states). If the history back event is triggered by browser we just need to close the modal if it is opened. The pushed history stack ensure that we stay on the same page.

Limitations: Adding a new history stack and going back in history also provide the opportunity to go forward in history (after closing the modal). This is not the desired behavior.

ng-bootstrap modal, now has dismissAll()

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

u can close all open modals by using a route-guard when ng-router detects a change (including browser-back)

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

and in router-config..

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

On Destroy Lifecycle hook will fix your problem.

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

}

LIVE DEMO

As far as I know, I think the answer to the OP's salient point:

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

... is that you can't cancel the back button event. As far as I know this is a browser useability restriction.

You need to add this code put in your dialog component.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM