Kesem David Kesem David - 3 months ago 39
Javascript Question

Collapse doesn't work in a Modal

I am trying to use the most basic collapse function inside a Modal but the collapse won't trigger

Literally just copied this w3schools collapse example into my Modal.

This is my Modal code:

<template #content let-c="close" let-d="dismiss" ngbModalContainer>
<div class="modal-header">
<h4 class="modal-title">Collapse</h4>
</div>
<form>
<div class="modal-body">
<button type="button" class="btn btn-info" data-toggle="collapse" data-target="#demo">Simple collapsible</button>
<div id="demo" class="collapse">
This is the collapsible text!
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="c('Close click')">Close</button>
</div>
</form>
</template>

<button class="btn btn-success" (click)="open(content)">Open Modal</button>


My BasicModalComponent:

@Component({
selector: 'basic-modal',
templateUrl: './BasicModal.html'
})
export class BasicModalComponent {
closeResult: string;

constructor(private modalService: NgbModal) {}

open(content) {
this.modalService.open(content).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
});
}

private getDismissReason(reason: any): string {
if (reason === ModalDismissReasons.ESC) {
return 'by pressing ESC';
} else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
return 'by clicking on a backdrop';
} else {
return `with: ${reason}`;
}
}
}


my AppComponent:

@Component({
selector: 'my-app',
template: '<basic-modal></basic-modal>'
})
export class AppComponent { }


My AppModule:

@NgModule({
imports: [NgbModule, FormsModule],
declarations: [AppComponent, BasicModalComponent],
bootstrap: [AppComponent],
})
export class AppModule {
}


I tried debugging the behavior of collapse in the DOM, seems like when you collapse a
<div>
it adds it some classes and few attributes, and when collapsing it back it changes them as well.

When I debugged it within the Modal, triggering the collapse button does not manipulate the DOM, the class of the
<div>
and its' attributes remained the same.

Any ideas?

Answer

In yout case you can "monkey patch" ModalWindow like as described below:

open(content) {
    // get reference to NgbModalRef
    let modal: any = this.modalService.open(content);
    modal.result.then((result) => {
      this.closeResult = `Closed with: ${result}`;
    }, (reason) => {
      this.closeResult = `Dismissed ${this.getDismissReason(reason)}`;
    });

    let cancelPropagation = false;

    // overriding methods of NgbModalWindow 
    Object.assign(modal._windowCmptRef.instance.constructor.prototype, {
        stopPropagation: () => {
          cancelPropagation = true;
        },
        backdropClick: function() {
          if(cancelPropagation) { 
            cancelPropagation = false;
            return;
          }
          if (this.backdrop === true) {
            this.dismiss(ModalDismissReasons.BACKDROP_CLICK);
          }  
        }
    });
  }

Plunker Example

But it is very dirty way because it's using private property.

You would use NgbCollapse directive which is part of ng-bootstrap package like:

<button type="button" class="btn btn-info" (click)="isCollapsed = !isCollapsed">
   Simple collapsible
</button>
<div [ngbCollapse]="isCollapsed">
   This is the collapsible text!
</div>

Plunker Example