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
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)
}
];
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 (e.g. 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With