You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换NgbNav标签后自定义Select组件选中值丢失问题求助

问题:自定义Select组件切换NgbNav标签后无法显示选中值

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 07:05:15