如何在API返回错误时阻止Modal弹窗关闭?
问题:食品项添加功能中Modal弹窗API报错时不关闭的实现问题
需求:实现食品项添加功能,点击添加按钮打开带表单的Modal弹窗,用户填写详情提交。后端重复项检查需等待API调用完成后才能获取结果,要求API返回错误时Modal弹窗不关闭,避免用户重新填写表单。使用第三方Modal和Toast库开发时遇到代码报错问题。
原代码文件
food.component.ts
addFood(data: any){ this.api.request('foodItem', 'POST', data).subscribe({ next: (response: any) => { if (response.message == 'Added Successfully.') { this._toastService.showSuccess('Food added.'); } else { this._toastService.showError(response.message); } }, complete: () => this.getFood(), error: (error: any) => this._toastService.showError(error.message) }); }
food-component.html
<app-custom-table [tableData]="tableData" [form]="formData" [columnArray]="columnArray" (onAdd)="addFood($event)" (onEdit)="editFood($event)"></app-custom-table>
custom-table.component.html
<button type="button" class="btn btn-link" (click)="openModal()" [disabled]="isRowSelected"><span>Add</span></button> <button type="button" class="btn btn-link" (click)="openModal(selectedRow)" [disabled]="isRowNotSelected"><i></i><span>Edit</span></button>
custom-table.component.ts
openModal(rowData: any = null) { const config: Partial<DynamicFormComponent> = { form: this.form, data: rowData }; const options: NgbModalOptions = { size: 'sm', centered: true }; const modalRef = this.ModalService.show((DynamicFormComponent), config, options). modalRef.componentInstance.submitSubject.subscribe((result: any) => { //此处报错:Property ComponentInstance does not exist in type Observable<ModalResult<unknown>> if (result.Success == true && result.Data) { if (rowData) { const updatedRowData = { ...rowData, ...result.Data }; // 若传入rowData则为编辑操作 this.onEdit.emit(updatedRowData); } else { //新增操作 this.onAdd.emit(result.Data); } } }); }
dynamic-form.component.ts(原标注为html,实际为ts文件)
@Output() submitSub: Subject<any> = new Subject<any>(); constructor( private activeModal: NgbActiveModal ){ } onSubmit(){ if(this.dynamicFormGroup.valid){ this.submitSubject.next(this.dynamicFormGroup.value) this.activeModal.close(this.dynamicFormGroup.value) //此处存疑 } }
更新代码后遇到的问题
更新后的custom-table.component.ts代码:
openModal(rowData: any = null) { const config: Partial<DynamicFormComponent> = { form: this.form, data: rowData }; const options: NgbModalOptions = { size: 'sm', centered: true, backdrop: 'static' }; this.ModalService. show((DynamicFormComponent), config, options); const activeSubscription = this.ngbModalService. activeInstances .subscribe((res: any) => { this.modalRef = res[0].componentInstance; }); setTimeout(() => { this.modalRef.componentInstance. submitSubject .subscribe((result: any) => { if (result.Success == true && result.Data) { if (rowData) { this.onEdit.emit(updatedRowData); } else { this.onAdd.emit(result.Data); } } } ); }); }
浏览器报错:无法读取未定义的属性'componentInstance'
解决方案
核心问题分析
ModalService.show()返回的是Observable而非直接的Modal实例,导致无法直接获取componentInstance- 通过
activeInstances和setTimeout获取实例的方式不可靠,存在异步时序问题 - 表单提交时自动关闭Modal,导致API报错后无法保留弹窗和表单内容
分步解决
1. 调整DynamicFormComponent的提交逻辑,取消自动关闭Modal
修改dynamic-form.component.ts:
import { Subject, Output } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @Output() submitSubject: Subject<any> = new Subject<any>(); constructor(private activeModal: NgbActiveModal) {} onSubmit(){ if(this.dynamicFormGroup.valid){ // 只发送表单数据,不自动关闭Modal this.submitSubject.next(this.dynamicFormGroup.value); } } // 新增手动关闭Modal的方法,供父组件调用 closeModal() { this.activeModal.close(); }
2. 正确获取Modal实例并监听提交事件
修改custom-table.component.ts:
import { take } from 'rxjs'; import { DynamicFormComponent } from './path-to-dynamic-form.component'; private currentDynamicForm: DynamicFormComponent | null = null; openModal(rowData: any = null) { const config: Partial<DynamicFormComponent> = { form: this.form, data: rowData }; const options: NgbModalOptions = { size: 'sm', centered: true, backdrop: 'static' // 禁止点击背景关闭弹窗 }; // 正确处理ModalService返回的Observable,获取组件实例 this.ModalService.show(DynamicFormComponent, config, options) .pipe(take(1)) // 只取一次结果,避免内存泄漏 .subscribe((modalResult) => { this.currentDynamicForm = modalResult.componentInstance as DynamicFormComponent; // 监听表单提交事件 this.currentDynamicForm.submitSubject.pipe(take(1)).subscribe((formData) => { if (rowData) { // 编辑操作:直接提交并关闭弹窗 const updatedRowData = { ...rowData, ...formData }; this.onEdit.emit(updatedRowData); this.currentDynamicForm?.closeModal(); } else { // 新增操作:仅发送数据,由API结果决定是否关闭弹窗 this.onAdd.emit(formData); } }); }); } // 新增方法,供父组件调用关闭当前Modal closeCurrentModal() { this.currentDynamicForm?.closeModal(); this.currentDynamicForm = null; }
3. 修改FoodComponent的addFood方法,控制Modal关闭时机
首先修改food-component.html,添加模板引用获取custom-table实例:
<app-custom-table #customTable [tableData]="tableData" [form]="formData" [columnArray]="columnArray" (onAdd)="addFood($event, customTable)" (onEdit)="editFood($event)"></app-custom-table>
然后修改food.component.ts的addFood方法:
addFood(data: any, customTable: CustomTableComponent){ this.api.request('foodItem', 'POST', data).subscribe({ next: (response: any) => { if (response.message == 'Added Successfully.') { this._toastService.showSuccess('Food added.'); // API成功后才关闭Modal customTable.closeCurrentModal(); this.getFood(); } else { this._toastService.showError(response.message); // 报错时不关闭Modal,保留表单内容 } }, error: (error: any) => { this._toastService.showError(error.message); // API请求失败时不关闭Modal } }); }
关键注意事项
- 确认第三方Modal库的
show()方法返回值结构,不同库的实例获取方式可能有差异,需参考对应文档 - 使用RxJS的
take(1)操作符避免内存泄漏 - 禁止表单提交时自动关闭Modal,将关闭逻辑完全交给API请求结果控制
- 保存当前Modal的组件引用,确保能在合适时机手动关闭
内容的提问来源于stack exchange,提问作者Sha Na
相关产品推荐
相关产品推荐

