调用startEditCard()时ngx-bootstrap Modal报backdrop未定义错误
I've run into this exact issue before with ngx-bootstrap modals, and it almost always boils down to the modal instance not being fully initialized when you try to call show(). Let's break down why this is happening with your EditPatientComponent and EditVisitComponent, and how to fix it.
Why This Error Happens
When you call this.editPatientComponent.showEditPatientModal() from PatientComponent, Angular might not have finished initializing the editCardModal (the ModalDirective in your child component) yet. The @ViewChild in EditPatientComponent only gets populated after the component's view has fully initialized (during the AfterViewInit lifecycle hook). Since AddVisitComponent works, it's likely that you're triggering its modal after the view is ready (e.g., via a user click that happens later), while startEditCard() might be called earlier.
Solution 1: Ensure Modal is Ready Before Showing It
Let's adjust the child component to wait for the modal to be initialized, then safely call show():
Step 1: Update EditPatientComponent
Add the AfterViewInit hook and a flag to track if the modal is ready:
import { Component, EventEmitter, Input, Output, ViewChild, AfterViewInit } from '@angular/core'; import { NgForm, FormGroup, FormBuilder } from '@angular/forms'; import { ModalDirective } from 'ngx-bootstrap/modal'; import { PatientCard } from "../../../models/patientcard.model"; @Component({ selector: 'editpatient', templateUrl: './editpatient.component.html', }) export class EditPatientComponent implements AfterViewInit { @Input('card') card: PatientCard; @Output() onEditingPatient = new EventEmitter<PatientCard>(); @ViewChild('editCardModal') editCardModal: ModalDirective; editCardForm: FormGroup; private isModalReady = false; // Track modal initialization constructor(private fb: FormBuilder) { // Initialize your form here if you haven't already this.editCardForm = this.fb.group({ card: [''] }); } ngAfterViewInit() { // This runs after the component's view is fully loaded this.isModalReady = true; // Optional: Patch form values when card is set (if needed) if (this.card) { this.editCardForm.patchValue(this.card); } } showEditPatientModal() { if (!this.isModalReady || !this.editCardModal) { // Retry after a short delay if modal isn't ready yet setTimeout(() => this.showEditPatientModal(), 100); return; } // Patch form values right before showing (in case card was updated recently) this.editCardForm.patchValue(this.card); this.editCardModal.show(); } // Add your editCard and closeEditCardModal methods here editCard(form: FormGroup) { this.onEditingPatient.emit(form.value); this.closeEditCardModal(); } closeEditCardModal() { this.editCardModal.hide(); } }
Step 2: Update PatientComponent's startEditCard Method
Make sure we only call the modal after the child component is ready:
startEditCard() { try { if (!this.editPatientComponent) { console.error('EditPatientComponent not found'); return; } this.editPatientComponent.card = this.card; // Trigger the modal show this.editPatientComponent.showEditPatientModal(); } catch (e) { console.error('Error opening edit modal:', e); alert(e); } }
Solution 2: Use BsModalService for Dynamic Modals (More Reliable)
If you want to avoid @ViewChild timing issues entirely, use ngx-bootstrap's BsModalService to dynamically load your modal component. This gives you full control over when the modal is initialized.
Step 1: Update PatientComponent to Use BsModalService
import { Component, OnInit, ViewChild } from '@angular/core'; import { PatientCard } from '../../models/patientcard.model'; import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal'; import { EditPatientComponent } from "./editpatient/editpatient.component"; @Component({ selector: 'patient', templateUrl: './patient.component.html' }) export class PatientComponent implements OnInit { // Remove the @ViewChild for EditPatientComponent // @ViewChild(EditPatientComponent) editPatientComponent: EditPatientComponent; private modalRef: BsModalRef; constructor(private modalService: BsModalService) {} startEditCard() { // Pass the card data to the modal component const initialState = { card: this.card }; // Open the modal with the EditPatientComponent this.modalRef = this.modalService.show(EditPatientComponent, { initialState }); // Listen for the output event from the modal this.modalRef.content.onEditingPatient.subscribe((updatedCard: PatientCard) => { this.editCard(updatedCard); this.modalRef.hide(); }); } // ... rest of your code }
Step 2: Simplify EditPatientComponent
Remove the ModalDirective reference and use BsModalRef to control the modal:
import { Component, Input, Output, EventEmitter } from '@angular/core'; import { FormGroup, FormBuilder } from '@angular/forms'; import { BsModalRef } from 'ngx-bootstrap/modal'; import { PatientCard } from "../../../models/patientcard.model"; @Component({ selector: 'editpatient', templateUrl: './editpatient.component.html', }) export class EditPatientComponent { @Input() card: PatientCard; @Output() onEditingPatient = new EventEmitter<PatientCard>(); editCardForm: FormGroup; constructor(public modalRef: BsModalRef, private fb: FormBuilder) { this.editCardForm = this.fb.group({ card: [''] }); // Patch form values when component initializes if (this.card) { this.editCardForm.patchValue(this.card); } } editCard(form: FormGroup) { this.onEditingPatient.emit(form.value); } closeEditCardModal() { this.modalRef.hide(); } }
Step 3: Remove Modal Markup from editpatient.component.html
You no longer need the bsModal wrapper—just keep the modal content:
<div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Редактирование карты</h5> <button type="button" class="close" (click)="closeEditCardModal()" aria-label="Close"></button> </div> <form [formGroup]="editCardForm" (ngSubmit)="editCard(editCardForm)"> <div class="modal-body"> <div class="form-group"> <label for="card">Карта</label> <input type="text" class="form-control" formControlName="card" id="card" required> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-warning" (click)="closeEditCardModal()">Закрыть</button> <button type="submit" class="btn btn-success" [disabled]="!editCardForm.valid">Редактировать</button> </div> </form> </div> </div>
Why AddVisitComponent Works
Chances are you're triggering AddVisitComponent's modal via a user interaction (like a button click) that happens after the parent component's view has fully initialized. By that time, the modal instance in AddVisitComponent is already ready, so calling show() doesn't throw an error.
内容的提问来源于stack exchange,提问作者MiNG

