使用PrimeNG Dropdown结合ngIf时FormControl变为undefined的问题
我基于PrimeNG Dropdown封装了自定义下拉组件,配合Reactive Forms的FormControl使用,组件接收父FormGroup和FormControlName作为输入。页面首次加载时,表单能通过API返回值正常赋值;但切换到不含该表单的标签页再返回,表单重新渲染后,原本存储对象值的FormControl变成了undefined。用原生HTML select搭配FormControl就正常,只有PrimeNG Dropdown存在这个问题。
代码示例
myComponent.component.html
<div *ngIf="isCohortDetailsTab" class="form-container p-20 height-100 overflow-y"> <form [formGroup]="cohortDetailsForm"> <app-dropdown [isFormField]="true" [dropdownItemsObj]="statuses" [parentFormGroup]="cohortDetailsForm" formCtrlName="statusFF" label="Status" nameField="name" placeholder="Select Status" [isRequired]="true" errorText="Status is required" ></app-dropdown> </form> </div>
myComponent.component.ts
const status: any = this.statuses.find((s: any) => s.id === this.eaCohortDetails.status_id); this.cohortDetailsForm.get('statusFF')?.setValue(status); // status 结构示例:{ id: 1, name: 'Draft' }
dropdown.component.html(用户误标注为.ts)
<form *ngIf="isFormField" [formGroup]="parentFormGroup" class="dropdown-form-control"> <label *ngIf="label">{{ label }}<span *ngIf="isRequired" class="required">*</span></label> <p-dropdown [options]="dropdownItemsArr" [(ngModel)]="selectedItem" [optionLabel]="nameField" [filter]="filter" [filterBy]="filterBy" [placeholder]="placeholder" (onChange)="onChange($event)" [title]="(selectedItem && selectedItem[nameField]) || placeholder" [showClear]="showClear" [formControlName]="formCtrlName" appendTo="body" [ngStyle]="{ 'cursor': disabled ? 'not-allowed' : '' }" > <ng-template pTemplate="selectedItem"> <div class="flex align-items-center gap-2" *ngIf="selectedItem"> <div>{{ selectedItem[nameField] }}</div> </div> </ng-template> <ng-template let-item pTemplate="item"> <div class="flex align-items-center gap-2"> <div>{{ item[nameField] }}</div> </div> </ng-template> </p-dropdown> <app-error-message *ngIf="parentFormGroup.controls[formCtrlName].invalid && (parentFormGroup.controls[formCtrlName].dirty || parentFormGroup.controls[formCtrlName].touched)" [errorText]="errorText" > </app-error-message> </form>
原因及解决方法
核心问题
同时使用[(ngModel)]双向绑定和Reactive Forms的[formControlName]会导致绑定冲突。当*ngIf重新渲染组件时,selectedItem初始值为undefined,双向绑定会把这个值同步到FormControl,导致原本的对象值被覆盖成undefined。原生select没有这个问题是因为它的表单绑定逻辑更简洁,不会和ngModel产生冲突。
具体解决步骤
移除
[(ngModel)]绑定
既然用了Reactive Forms,就不需要再用ngModel,两者不能混用。删除<p-dropdown>上的[(ngModel)]="selectedItem"。通过FormControl同步选中值
在自定义dropdown组件中,通过父FormGroup获取对应控件,订阅其值变化来更新selectedItem:// dropdown.component.ts import { OnInit, OnDestroy } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { Subscription } from 'rxjs'; export class DropdownComponent implements OnInit, OnDestroy { @Input() parentFormGroup: FormGroup; @Input() formCtrlName: string; @Input() nameField: string; selectedItem: any; private valueSubscription: Subscription; ngOnInit(): void { // 初始同步FormControl的值 this.selectedItem = this.parentFormGroup.get(this.formCtrlName)?.value; // 订阅值变化,实时更新selectedItem this.valueSubscription = this.parentFormGroup.get(this.formCtrlName)?.valueChanges.subscribe(val => { this.selectedItem = val; }); } ngOnDestroy(): void { // 销毁订阅避免内存泄漏 this.valueSubscription?.unsubscribe(); } onChange(event: any): void { // 直接通过FormControl更新值,无需依赖ngModel this.parentFormGroup.get(this.formCtrlName)?.setValue(event.value); } }确保选项数组初始化完成
检查dropdownItemsArr是否在组件初始化时已经从dropdownItemsObj正确转换完成,保证PrimeNG Dropdown渲染时已有完整的选项列表,否则无法匹配选中值。添加
compareWith函数(处理对象引用不匹配)
如果API返回的对象和statuses数组中的对象不是同一个引用(比如是新实例化的对象),PrimeNG Dropdown无法自动识别匹配,需要添加对比函数:
在<p-dropdown>上添加:[compareWith]="compareObjects"然后在组件类中定义对比逻辑:
compareObjects(obj1: any, obj2: any): boolean { // 按id匹配对象,可根据实际字段调整 return obj1 && obj2 ? obj1.id === obj2.id : obj1 === obj2; }
内容的提问来源于stack exchange,提问作者Jainam Parikh

