ngFor with ngBootstrap NgbModal Open - Angular Bootstrap

326 views Asked by At

I am having an issue with calling an NgbModal open() within an item rendered within a *ngFor loop.

I have a list of items that I want to display. On click of said item, I want a modal to open in order to prompt the user for some additional information.

When clicking on the item, I can see the elements get added to the DOM via the DevTools, but the modal does not have the show class added to it. The component that should be rendered as the modal's contents is not properly initialized either. If I add the show class manually to it, I can see the modal's contents but the component does not work appropriately.

It is important to note that for testing, I added a button subscribed to a click event which passes a fake item to the same function, setSelected(). This method of opening the modal works without a problem. The issue only manifests itself when it is within the *ngFor.

I have a console.log inside of ConnectionModalComponent's (which is being passed as the modal's contents) constructor and another in it's ngAfterViewInit. When activating the modal via a button outside of the *ngFor, I see both log statements in the console, but when activating the modal from within the *ngFor I only see the constructor's log statement and not the latter.

I am using @angular/[email protected] & @ng-bootstrap/[email protected].

testComponent.html.ts - Component that has the *ngFor

        <div>
          <div
            *ngFor="let item of items"
            (click)="setSelected(item)"
          >
            <span class="full-height">
              <div class="card-body">
                <div class="card-title">
                  {{ item.nickname }}
                </div>
              </div>
            </span>
          </div>
        </div>

testComponent.component.ts - Function that is called from the HTML template.

  public setSelected(item: ItemViewModel): void {
    this.testAlertService.open();
  }

testAlert.service.ts - Service that is called from the component in order to open the NgbModal.

import { Injectable } from '@angular/core';
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';

import { ConnectionModalComponent } from '../../components/shared';

@Injectable()
export class TestAlertService {
  constructor(private modalService: NgbModal) {}

  open() {
    this.modalService.open(ConnectionModalComponent, { ariaLabelledBy: 'modal-basic-title' }).result.then(
      (result) => {
        console.log(`Closed with: ${result}`);
      },
      (reason) => {
        console.log(`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}`;
    }
  }
}
1

There are 1 answers

0
ericegonz On

I was able to resolve the issue.

Inside of testComponent.component.ts where I was pushing to the items array, I was using a detectChanges() method on the ChangeDetectorRef class.

By simply wrapping the array mutation to use NgZone's run() solved all my problems.