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

Angular 14自定义mat-select无法展开显示选项排查求助

问题描述

基于Angular 14开发了一个可复用的mat-select组件库,目前遇到mat-select无法展开显示选项的问题,控制台没有报错。想请教这是否与NG_VALUE_ACCESSOR有关?或是Angular Material在Angular 14版本中有相关变更?

相关代码

lib-mat-select.html

<mat-form-field appearance="legacy">
    
      <!-- label -->
      <mat-label>Label</mat-label>
    
      <!-- icon -->
      <mat-icon *ngIf="controler.value && !controler.disabled" (click)="clearInput(); 
        $event.stopPropagation()">
        highlight_off
      </mat-icon>
    
      <!-- mat-select -->
      <mat-select [formControl]="controler" disableOptionCentering="true" panelClass="panelClass"
        (selectionChange)="onSelectionChange($event)">
    
        <!-- mat-options -->
        <mat-option *ngFor="let item of options | keyvalue" [value]="item.key">
          {{item.value}}
        </mat-option>
    
      </mat-select>
    
    </mat-form-field>

lib-mat-select.ts

@Component({
  selector: "lib-mat-select",
  templateUrl: "./lib-mat-select.component.html",
  styleUrls: ["./lib-mat-select.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => LibMatSelectComponent),
      multi: true,
    },
  ],
})
export class LibMatSelectComponent implements ControlValueAccessor, OnInit {
  @Input() public formControlName: string;
  @Input() public label: string;
  @Input() public placeholder: string;
  @Input() public options: any;

  @Output() public selectionChangeEmitter = new EventEmitter();

  public controler: FormControl;

  constructor(private controlContainer: ControlContainer) {}

  ngOnInit(): void {
    this.initController();
  }

  public onSelectionChange(event) {
    this.selectionChangeEmitter.emit(event);
  }

  private initController() {
    if (this.controlContainer && this.formControlName) {
      this.controler = this.controlContainer.control?.get(
        this.formControlName
      ) as FormControl;
    }
  }

  public clearInput() {
    this.controler.setValue(null);
  }

  // Les méthodes de l'interface ControlValueAccessor qui ne nécessite pas spécialement d'être surchagée
  writeValue(obj: any): void {}

  registerOnChange(fn: any): void {}

  registerOnTouched(fn: any): void {}
}

app.component.html

<form [formGroup]="form">

  <div class="center-screen">
    <lib-mat-select formControlName="fruits" [options]="fruitList"></lib-mat-select>
  </div>

</form>

app.component.ts

fruitList: { 1: 'Pomme', 2: 'Orange', 3: 'Framboise'}

  public form: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.createForm();
  }

  public createForm() {
    this.form = this.fb.group({
      fruits: null,
    });
  }

解决方案

问题核心在于组件同时实现ControlValueAccessor又通过controlContainer绑定外部表单控件,两种表单状态管理方式冲突,导致mat-select交互逻辑异常,和Angular Material 14版本变更无关。

具体问题点

  1. 注册NG_VALUE_ACCESSOR意味着组件要作为自定义表单控件独立管理状态,但你却直接将外部FormControl绑定到内部mat-select,导致控件状态脱节,下拉面板触发逻辑失效。
  2. 空实现的ControlValueAccessor方法没有起到任何作用,反而干扰了Angular表单的状态同步机制。

修复方案(二选一)

方案1:移除ControlValueAccessor,保留外部表单绑定

适合组件依赖父表单的场景:

  1. 删除组件providers中的NG_VALUE_ACCESSOR配置,去掉implements ControlValueAccessor及对应空方法。
  2. 修复模板中硬编码的label,绑定到组件输入属性:
    <mat-label>{{label}}</mat-label>
    
  3. 为controler添加兜底逻辑,避免空值报错:
    private initController() {
      if (this.controlContainer && this.formControlName) {
        this.controler = this.controlContainer.control?.get(this.formControlName) as FormControl;
      } else {
        this.controler = new FormControl();
      }
    }
    

修改后的lib-mat-select.ts核心代码:

@Component({
  selector: "lib-mat-select",
  templateUrl: "./lib-mat-select.component.html",
  styleUrls: ["./lib-mat-select.component.scss"],
})
export class LibMatSelectComponent implements OnInit {
  @Input() public formControlName: string;
  @Input() public label: string;
  @Input() public placeholder: string;
  @Input() public options: any;

  @Output() public selectionChangeEmitter = new EventEmitter();

  public controler: FormControl;

  constructor(private controlContainer: ControlContainer) {}

  ngOnInit(): void {
    this.initController();
  }

  public onSelectionChange(event) {
    this.selectionChangeEmitter.emit(event);
  }

  private initController() {
    if (this.controlContainer && this.formControlName) {
      this.controler = this.controlContainer.control?.get(
        this.formControlName
      ) as FormControl;
    } else {
      this.controler = new FormControl();
    }
  }

  public clearInput() {
    this.controler.setValue(null);
  }
}

方案2:正确实现ControlValueAccessor,作为独立自定义控件

适合组件需要脱离父表单单独使用的场景:

  1. 保留NG_VALUE_ACCESSOR配置,内部维护FormControl,正确实现所有ControlValueAccessor方法。
  2. 监听内部控件变化,同步状态到外部。

修改后的lib-mat-select.ts核心代码:

@Component({
  selector: "lib-mat-select",
  templateUrl: "./lib-mat-select.component.html",
  styleUrls: ["./lib-mat-select.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => LibMatSelectComponent),
      multi: true,
    },
  ],
})
export class LibMatSelectComponent implements ControlValueAccessor {
  @Input() public label: string;
  @Input() public placeholder: string;
  @Input() public options: any;

  @Output() public selectionChangeEmitter = new EventEmitter();

  public controler = new FormControl();
  private onChange: (value: any) => void = () => {};
  private onTouched: () => void = () => {};

  ngOnInit(): void {
    this.controler.valueChanges.subscribe(value => {
      this.onChange(value);
      this.selectionChangeEmitter.emit(value);
    });
  }

  public clearInput() {
    this.controler.setValue(null);
    this.onTouched();
  }

  writeValue(obj: any): void {
    this.controler.setValue(obj);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    isDisabled ? this.controler.disable() : this.controler.enable();
  }
}

此时组件可直接绑定formControl或ngModel:

<lib-mat-select [formControl]="fruitsControl" [options]="fruitList"></lib-mat-select>

内容的提问来源于stack exchange,提问作者foo42

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 23:06:09