Angular: dismiss modal when browser back button pressed and cancel navigation

Viewed 1219

I'm trying to dismiss a full-window modal when the browser back button is pressed with this code:

class MyModalComponent {
  constructor(private _location: LocationStrategy) { }

  public ngOnInit(): void {
    this._location.pushState(this.url, this.title, this.url, '');
  }

  @HostListener('window:popstate', ['$event'])
  public onPopState(event: PopStateEvent) {
      this.modal.dismiss();
      event.preventDefault();
      event.stopPropagation();
      event.cancelBubble = true;
  }
}

When the back button is pressed, onPopState is invoked and the modal is dismissed, but also Angular Router detects the URL change and starts navigating to the previous state. Is there any way to cancel the navigation or prevent Angular Router listening to URL changes when a modal is open?

2 Answers

One dirty solution could be using onSameUrlNavigation router property set to 'ignore' and pushing the current URL into the navigation history to prevent back button invoking a router navigation:

class MyModalComponent {
  private _prevOnSameUrlNavigation; 

  constructor(private _location: LocationStrategy, private _router: Router) {

  }

  public ngOnInit(): void {
    this._prevOnSameUrlNavigation = this._router.onSameUrlNavigation;
    this._router.onSameUrlNavigation = 'ignore';
    this._location.pushState("Modal state", "Modal title", this._router.url, '');
  }

  @HostListener('window:popstate', ['$event'])
  public onPopState(event: PopStateEvent) {
      this.modal.dismiss();
      event.preventDefault();
      event.stopPropagation();
      event.cancelBubble = true;
  }

  public ngOnDestroy() {
      this._router.onSameUrlNavigation = this._prevOnSameUrlNavigation;
  }
}

Ive got workaround. You need to manually remove modal parts like so...

import {Component, OnInit} from '@angular/core';
import { Router, NavigationEnd } from "@angular/router";

import { map } from 'rxjs';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  loading: boolean = false;

  constructor(
    private _loading: LoadingService,
    private router: Router
  ){
    router.events.subscribe((val) => {
        if(val instanceof NavigationEnd) {
          if(document.body.classList[0] == 'modal-open')  {
            document.body.classList.remove("modal-open");
            const over = document.getElementsByClassName('modal-backdrop')[0]
            if(over){
              over.parentNode!.removeChild(over);
            }
          }
        }
    });
  }
Related