Angular可复用FormGroup选择组件无法设置默认值问题
Angular可复用选择组件默认值不生效问题修复方案
问题分析
你开发的可复用选择组件实现了NG_VALUE_ACCESSOR接口,当表单预设默认值时writeValue能正确接收参数,但选择框显示为空。核心原因有两点:
- 内部
FormControl的变化未正确同步到外部表单,且异步加载的选项可能在writeValue执行后才渲染,导致值设置后无法匹配到选项。 writeValue设置值时触发了不必要的事件循环,同时缺少对选项加载完成后的同步逻辑。
具体修改点
1. 组件TS文件修改 (select.component.ts)
import {Component, forwardRef, Input, OnInit, AfterContentChecked, ContentChildren, QueryList, ElementRef} from '@angular/core'; import {FormControl, NG_VALUE_ACCESSOR} from '@angular/forms'; import {faChevronDown} from '@fortawesome/pro-solid-svg-icons'; @Component({ selector: 'component-select', templateUrl: './select.component.html', styleUrls: ['./select.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, multi: true, useExisting: forwardRef(() => SelectComponent), } ] }) export class SelectComponent implements OnInit, AfterContentChecked { @Input() formControlName: string; public formControl: FormControl = new FormControl(); public onChange: (_: any) => void = () => {}; public onTouched: () => void = () => {}; // 监听所有子选项元素 @ContentChildren('selectOption', {descendants: true}) options: QueryList<ElementRef>; constructor() { } ngOnInit(): void { // 监听内部控件值变化,同步到外部表单 this.formControl.valueChanges.subscribe(value => { this.onChange(value); }); } ngAfterContentChecked(): void { // 当选项加载完成后,重新同步值,确保选中状态生效 if (this.formControl.value !== null && this.options.length > 0) { this.formControl.setValue(this.formControl.value, {emitEvent: false}); } } writeValue(obj: any): void { // 设置值时禁止触发事件,避免重复同步 this.formControl.setValue(obj, {emitEvent: false}); } registerOnChange(fn: any): void { this.onChange = fn; } registerOnTouched(fn: any): void { this.onTouched = fn; } setDisabledState?(disabled: boolean): void { disabled ? this.formControl.disable({emitEvent: false}) : this.formControl.enable({emitEvent: false}); } protected readonly faChevronDown = faChevronDown; }
2. 组件HTML文件修改 (select.component.html)
<component-label><ng-content select="[slot=label]"></ng-content></component-label> <div class="input-container"> <div class="left-icon"> <ng-content select="[slot=left-icon]"></ng-content></div> <!-- 移除change事件,改为通过valueChanges监听;添加blur事件触发onTouched --> <select class="form-input highlight-on-error" [formControl]="formControl" (blur)="onTouched()"> <ng-content></ng-content> </select> <div class="right-icon"><fa-icon [icon]="faChevronDown"></fa-icon></div> </div> <component-error-message [controlName]="formControlName"></component-error-message>
3. 使用示例调整
在使用组件的模板中,给每个option添加模板引用#selectOption:
<component-select formControlName="nationality"> <ng-container slot="label">{{ 'NATIONALITY_LABEL' | translate }}</ng-container> <option #selectOption [ngValue]="null"></option> <option #selectOption [ngValue]="option.code" *ngFor="let option of countries$ | async">{{ option.name }}</option> </component-select>
修改说明
- 新增
AfterContentChecked钩子,在选项加载完成后重新同步值,解决异步选项导致的默认值不显示问题。 - 监听内部
FormControl的valueChanges,确保值变化同步到外部表单,同时移除HTML中的change事件避免重复触发。 writeValue和setDisabledState中添加emitEvent: false,防止不必要的事件循环。- 通过
ContentChildren监听选项元素变化,确保选项更新后能正确匹配值。
内容的提问来源于stack exchange,提问作者Saidamir
相关产品推荐
相关产品推荐

