切换NgbNav标签后自定义Select组件选中值丢失问题求助
自定义Select组件通过formControlName能正常将选项值存入FormControl,但切换NgbNav标签后,组件不再显示之前选中的值。调试确认writeValue方法能接收到正确的obj,但无法同步到Select控件上。
相关代码
NgbNav页面HTML代码
<ul ngbNav #nav="ngbNav" class="nav-tabs"> <li ngbNavItem> <button ngbNavLink>Dados do representante</button> <ng-template ngbNavContent> <form [formGroup]="representativeForm"> <section> <div class="row"> <div class="col-sm-12 col-md-6 col-lg-4"> <b-select-custom id="sex" name="sex" [options]="sexs" propertyOption="description" label="Sexo" formControlName="sex" [isValid]="representativeForm.controls['sex'].errors?.['required'] && representativeForm.controls['sex'].touched"></b-select-custom> <!-- <select name="sex" id="sex" formControlName="sex"> <option value="" selected></option> <option *ngFor="let sex of sexs" [ngValue]="sex">{{sex.description}}</option> </select> --> </div> <div class="col-sm-12 col-md-6 col-lg-4"> <b-select-custom id="peopleType" name="peopleType" [options]="peopleTypes" propertyOption="description" label="Tipo de pessoa" formControlName="peopletype" [isValid]="representativeForm.controls['peopletype'].errors?.['required'] && representativeForm.controls['peopletype'].touched" (ngModelChange)="filterDocumentTypeByPeopleTypeId($event)"> </b-select-custom> </div> </div> </section> <section class="mt-3"> <div class="row"> <div class="col-sm-6 col-md-6 col-lg-4"> <b-select-custom id="documentType" name="documentType" label="Tipo de documento" formControlName="documenttype" [options]="documentTypesFiltered" propertyOption="description" [isValid]="representativeForm.controls['documenttype'].errors?.['required'] && representativeForm.controls['documenttype'].touched"></b-select-custom> </div> </div> </section> </form> </ng-template> </li> <li ngbNavItem> <button ngbNavLink (click)="loadStates()">Dados do Endereço</button> <ng-template ngbNavContent> </ng-template> </li> <li ngbNavItem> <button ngbNavLink>Dados de usuário</button> <ng-template ngbNavContent> <button (click)="onSubmit()">Cadastrar</button> </ng-template> </li> </ul> <div id="ngbNavOutlet" [ngbNavOutlet]="nav"></div>
NgbNav页面TS代码
protected sexs: Array<Sex> = [{id:1, "Feminino"},{id:2, description: "Masculino"}]; representativeForm: FormGroup = new FormGroup({ sex: new FormControl<string | null>(null, [Validators.required])});
自定义Select组件HTML代码
<div class="container-select-wrapper w-100"> <div class="select-wrapper w-100 d-flex align-items-center" [ngClass]="{'has-value':hasValue}"> <select [id]="id" (change)="onSelectOption($event)" class="w-100" [disabled]="disabled" (blur)="onTouched()"> <option [value]="" selected></option> <option *ngFor="let option of options" [value]="option | json">{{option[propertyOption]}} </option> </select> <label [for]="id" class="floating-label">{{label}}</label> <img id="iconAlert" *ngIf="isValid" title="Campo obrigatório" src="\assets\icons\alert.svg" /> </div> </div>
自定义Select组件TS代码
import { Component, forwardRef, Input } from '@angular/core'; import { ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; @Component({ selector: 'b-select-custom', templateUrl: './select-custom.component.html', styleUrls: ['./select-custom.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectCustomComponent), multi: true }, { provide: NG_VALIDATORS, useExisting: forwardRef(() => SelectCustomComponent), multi: true } ] }) export class SelectCustomComponent implements ControlValueAccessor, Validator { @Input() id!: string; @Input() label!: string; @Input() options!: any[]; @Input() formControlName!: string; @Input() propertyOption!: string; @Input() isValid!: boolean; selectedOption: any; onChange: any; onTouched: any; hasValue: boolean = false; disabled: boolean = false; writeValue(obj: any): void { this.selectedOption = obj; } registerOnChange(fn: any): void { this.onChange = fn; } registerOnTouched(fn: any): void { this.onTouched = fn } setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled; } validate(): any { return null; } onSelectOption(option: any): void { const select = (option.target as HTMLSelectElement) this.selectedOption = JSON.parse(select.value); this.hasValue = this.selectedOption != ""; this.onChange(this.selectedOption); this.onTouched(); } }
解决方案
问题核心是自定义组件未将selectedOption与原生select控件做双向绑定,切换NgbNav标签时组件重新渲染,writeValue接收到值但无法同步到UI。
1. 修正原生select的绑定逻辑
修改自定义组件HTML中的select标签,使用[(ngModel)]绑定selectedOption,同时移除默认的selected属性避免冲突:
<select [id]="id" [(ngModel)]="selectedOption" (change)="onSelectOption($event)" class="w-100" [disabled]="disabled" (blur)="onTouched()"> <option [value]=""></option> <option *ngFor="let option of options" [value]="option | json">{{option[propertyOption]}}</option> </select>
2. 优化writeValue方法同步状态
在writeValue中更新hasValue,确保标签样式和选中状态同步:
writeValue(obj: any): void { this.selectedOption = obj; this.hasValue = !!obj; // 存在值时标记为有状态 }
3. 简化onSelectOption方法(可选)
由于已使用双向绑定,可简化事件处理逻辑:
onSelectOption(): void { this.hasValue = !!this.selectedOption; this.onChange(this.selectedOption); this.onTouched(); }
额外说明
NgbNav默认会销毁非活动标签的内容,切换回时重新创建组件实例。修正绑定后,writeValue触发时selectedOption会直接同步到原生select,解决选中值不显示的问题。
内容的提问来源于stack exchange,提问作者Rafael Massula
相关产品推荐
相关产品推荐

