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}`;
}
}
}
I was able to resolve the issue.
Inside of
testComponent.component.ts
where I was pushing to theitems
array, I was using adetectChanges()
method on theChangeDetectorRef
class.By simply wrapping the array mutation to use
NgZone
'srun()
solved all my problems.