自定义输入组件嵌套PrimeNG Dropdown遭遇三类问题求助
问题1:下拉框自动选中第一个值的异常
原因
自定义组件同时使用ControlValueAccessor和[(ngModel)]绑定ddValue,初始值为undefined时,PrimeNG Dropdown会默认选中第一个选项;且ddValue与组件内部的_value未同步,导致状态不一致。
解决方案
移除双向绑定[(ngModel)],改用单向绑定[value],通过ControlValueAccessor逻辑同步值,确保ddValue与_value状态一致:
my-own-input.component.html
<p-dropdown [inputId]="'clothDropdown'" [name]="'clothDropdown'" [options]="options" [optionLabel]="optionLabel" [value]="ddValue" [placeholder]="placeholder" [showClear]="false" (onChange)="dropdownChanged($event.value)"> </p-dropdown>
my-own-input.component.ts
set value(value) { this._value = value; this.ddValue = value; // 同步ddValue与组件值 this.onChange(value); this.onTouched(); this.cd.detectChanges(); } writeValue(value: any) { this._value = value; this.ddValue = value; // 初始化时同步值 this.cd.detectChanges(); }
问题2:自定义输入组件的(change)事件无法触发
原因
自定义组件作为ControlValueAccessor,未对外暴露change事件发射器,仅调用表单控件内部的onChange回调,无法触发父组件绑定的(change)事件。
解决方案
添加change事件发射器,并在值变更时触发:
my-own-input.component.ts
// 新增Output事件 @Output() change = new EventEmitter<any>(); dropdownChanged(value: any) { this.value = value; this.change.emit(value); // 触发父组件绑定的change事件 }
问题3:从父组件传递PrimeNG Dropdown模板到自定义组件内部
解决方案
通过ContentChild捕获父组件传入的模板,再通过ngTemplateOutlet传递给内部p-dropdown,同时保留模板上下文支持:
my-own-input.component.ts
// 新增ContentChild捕获父组件模板 @ContentChild('selectedItemTemplate') selectedItemTemplate: TemplateRef<any> | undefined; @ContentChild('itemTemplate') itemTemplate: TemplateRef<any> | undefined;
my-own-input.component.html
<p-dropdown [inputId]="'clothDropdown'" [name]="'clothDropdown'" [options]="options" [optionLabel]="optionLabel" [value]="ddValue" [placeholder]="placeholder" [showClear]="false" (onChange)="dropdownChanged($event.value)"> <!-- 传递选中项模板 --> <ng-template *ngIf="selectedItemTemplate" pTemplate="selectedItem" let-value> <ng-container [ngTemplateOutlet]="selectedItemTemplate" [ngTemplateOutletContext]="{ $implicit: value }"></ng-container> </ng-template> <!-- 传递下拉项模板 --> <ng-template *ngIf="itemTemplate" pTemplate="item" let-item> <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item }"></ng-container> </ng-template> </p-dropdown>
父组件调用示例(parent.component.html)
<form [formGroup]="someService.selectedCloth"> <app-my-own-input formControlName="cloth" inputType="dropdown" [options]="someService.someOptions" optionLabel="clothName" placeholder="Select stuff" (change)="getClothCost()"> <!-- 选中项模板,用#标记供子组件捕获 --> <ng-template #selectedItemTemplate let-value> <div>{{ value?.clothName }}</div> </ng-template> <!-- 下拉项模板,支持自定义上下文变量 --> <ng-template #itemTemplate let-oneCloth> <div [class]="!oneCloth.id ? 'grey-text' : ''"> {{ oneCloth.clothName }} </div> </ng-template> </app-my-own-input> </form>
说明
父组件通过模板引用标记(#selectedItemTemplate、#itemTemplate)定义自定义模板,子组件捕获后通过ngTemplateOutlet传递,同时通过ngTemplateOutletContext将Dropdown的value/item传递给父模板的上下文变量,实现模板通用性适配不同对象结构。
内容的提问来源于stack exchange,提问作者Emiliorth

