ngx-bootstrap: Modal keeps body class `modal-open` on after closing, causing scrollbars to disappear

Description

When you open and close modals, the <body> tag still has the CSS class modal-open.

This has the unfortunate effect of disabling scrolling in the page, because of the style
body.modal-open {overflow: hidden; }, which is correct when the modal is open, just not when it’s closed.

How to reproduce

Generate an Angular CLI application, and add ngx-bootstrap 1.7.1.

Add a modal popup, show it and hide it, and inspect the <body> tag in browser devtools.

Notes

As mentioned in https://github.com/valor-software/ngx-bootstrap/pull/1987#issuecomment-311391785, I think this was introduced as part of implementing the nested modals support. It seems that when the modal is animated, which is the default, something happens and the component thinks it’s nested, and does not clear the modal-open CSS class.

A possible workaround is to disable animations, which can be done globally by adding a directive like:

import { Directive } from '@angular/core';
import { ModalDirective } from 'ngx-bootstrap/modal/modal.component';

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[bsModal]',
  exportAs: 'bs-modal-override'
})
export class BsModalOverrideDirective {
  constructor(private modal: ModalDirective) {
    modal.isAnimated = false;
  }
}

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 4
  • Comments: 33 (6 by maintainers)

Most upvoted comments

i still can see the issue in 1.8.1

Closing, until we have something reproducible 😃

Still have this issue using ngx-bootstrap using 6.2.0

My use case is slightly different than what is described above, but symptom is the same. The body tag is incorrectly marked with “model-open” class, when no modal is open.

My use case is:

  1. Component A opens up a shared modal dialog A
  2. User closes modal dialog A
  3. Upon exiting modal, app is immediately routed to Component B which in turn immediately opens up shared modal dialog A again.
  4. User closes modal dialog A again
  5. The body tag still has “modal-open” class

After tracing through the ngx-bootstrap-modal.js, I discovered that even though the 2nd modal dialog is NOT nested, ngx-bootstrap thinks it is because in the “show()” method, the “modal-open” class is still attached to the body element as a result of the opening of the 1st modal dialog. And when that 2nd modal is closed, because it has been marked as isNested = true, the “hideModal()” method does not remove the “modal-open” class or call resetScrollbar(). Now you are stuck, because both modal dialogs are closed, but the class is still applied.

I think the root of the issue is that the existence of the class “modal-open” is essentially the arbiter of whether or not a modal is considered to be nested. Problem is, that class only gets removed as part of the hideModal() method. And that method gets executed inside of the hide() method on a 300 ms delay, if config.animated == true (which it is by default). And the execution of hideModal() actually gets cancelled by the show() method call of the 2nd modal.

So the upshot is, if you call show() on a modal dialog within 300 ms of calling hide() on a modal dialog. You willl end up with this problem.

I can workaround this by setting config.animated = false, because it effectively gets rid of that 300 ms delay.

This issue is still present in 6.1.0. At this point I’m going to have to stop using this package. It’s become unbearable for my users.

I’m using version 5.1.2 and am also experiencing this issue. Updated to 5.5.0 and the issue still occurs.

Solution

Was with this problem. It turned out that I imported the module twice, which contained imports to the ModalModule.

Make sure the ModalModule is only imported once on all project.

Everyone sees this issue, and we all agree that the timeout is causing the nested logic to get tripped up and mark dialogs nested when they aren’t. I’m going to repost this solution I outlined years ago:

The BsModalService has a dialog counter which would be what you need to counteract the problem because whomever is the last dialog dismissed can remove modal-open class regardless of the order the dialogs are opened and closed. So it seems all of the pieces are there to fix it. I’d remove the nested flag, inject BsModalService, and add code to remove the class when modalCount() reaches zero.

at version 5.1.1 we still have this issue.

export class LoadingSpinnerComponent implements OnInit, OnChanges {
  @Input() enable: boolean;
  @Input() type = 'loading';
  @ViewChild('staticModal', { static: true }) staticModal: ModalDirective;
  constructor() {}

  ngOnInit() {}

  ngOnChanges(changes: SimpleChanges) {
    if (changes['enable'] && changes['enable'].currentValue === true) {
      if (this.staticModal.isShown) {
        this.staticModal.hide();
        document.body.classList.remove('modal-open');
      }

      this.staticModal.config = Object.assign(
        {},
        { backdrop: true, ignoreBackdropClick: true }
        );
        this.staticModal.show();
      } else {
        if (this.staticModal.isShown) {
          this.staticModal.hide();
          document.body.classList.remove('modal-open');
        }
      }
    }

}

this loader is being dynamically. we resulted to a work around which is being recommended. document.body.classList.remove('modal-open')

my solution was to use BsModalService to load only one modal template at a time and to gain more control over events and triggers.

On Tue, Oct 3, 2017 at 3:19 PM, Henry Ollarves notifications@github.com wrote:

Any workarounds for this issue? Been bashing my head for 2 days now trying to make it work. I am setting backdrop to false (since it’s also not hiding the backdrop when the .hide() method is called) I’m using the component approach, and I’m injecting data properly. However, even though the modal disappears, the backdrop never does, and the body maintains the modal-open class. The modal-container element also remains in the DOM.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/valor-software/ngx-bootstrap/issues/2137#issuecomment-333824335, or mute the thread https://github.com/notifications/unsubscribe-auth/ABvuMbcOxHgDAJiB9NqPpQuleDswVg6pks5soiZdgaJpZM4OHU-W .

fixed in v1.8