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

Angular Material Autocomplete closePanel方法失效问题求助

Angular Mat-Autocomplete 编辑场景下面板自动展开问题

我正在开发一个带对话框的表单,其中一个表单项使用了mat-autocomplete。提交后的表单会添加到表格中,点击表格行时会重新打开表单对话框进行编辑。

当前问题

打开编辑表单时,autocomplete字段已填充之前提交的值,但autocomplete面板会自动展开,不符合预期。

期望效果

字段正常填充历史值,autocomplete面板保持关闭状态。

已尝试的方案

  1. 使用@ViewChild(MatAutocompleteTrigger) autoComplete: MatAutocompleteTrigger;,调用this.autoComplete.closePanel();
  2. 在HTML的input上尝试#autoComplete或#MatAutocompleteTrigger选择器
  3. 尝试过以下写法:
    HTML:
    <input #autoCompleteInput type="text" [matAutocomplete]="auto"/>
    
    TS:
    @ViewChild('autoCompleteInput', { read: MatAutocompleteTrigger }) 
    autoComplete: MatAutocompleteTrigger;
    

但实际运行报错:ERROR TypeError: Cannot read properties of undefined (reading 'closePanel'),因为autoComplete实例为undefined。

以下是相关代码片段:

HTML(仅autocomplete部分)

<mat-form-field class="top-form-field" appearance="outline">
  <mat-label>Brigada</mat-label>
  <input matInput
         #autoComplete
         aria-label="Brigada"
         [matAutocomplete]="auto"
         formControlName="nome_brigada">
  <mat-autocomplete #auto="matAutocomplete">
    <mat-option *ngFor="let brigade of filteredBrigades | async" [value]="brigade.name">
      <img class="option-img" aria-hidden [src]="brigade.typeIcon" height="25">
      <span>{{brigade.name}}</span> |
      <small>{{brigade.category}}</small>
      <span *ngIf="brigade.plate">
        | <small>{{brigade.plate}}</small>
      </span>
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

TS代码(不含导入)

注:当row不为null时,为编辑已提交表单的场景(即需要关闭面板的场景)

export interface Brigade {
  type: string;
  typeIcon: string;
  name: string;
  category: string;
  filterString: string;
  id_brigada: string;
  plate: string;
}

@Component({
  selector: 'select-brigade-dialog',
  templateUrl: 'select-brigade-dialog.html',
})
export class SelectBrigadeDialog {

  @ViewChild(MatAutocompleteTrigger) autoComplete: MatAutocompleteTrigger;

  public brigadeForm: FormGroup;
  brigadeCtrl = new FormControl('', [Validators.required]);
  filteredBrigades: Observable<Brigade[]>;

  public brigades: Brigade[] = [];

  public tipos_brigada:string[];
  public categorias_brigada:string[];
  public nomes_brigada: string[];

  combateChecked: boolean;
  rescaldoChecked: boolean;
  
  constructor(
    @Inject(MAT_DIALOG_DATA)
    public data: any,
    public fb: FormBuilder,
    private dtValidators: DatetimeValidators,
    private amplitude: AmplitudeService
  ) {

    this.combateChecked = false;
    this.rescaldoChecked = false;

    for (let b of data.brigadas){
      this.brigades.push({
        type: b['tipo_brigada'],
        typeIcon: brigadeIconMap[b['id_tipo_brigada']],
        name: b['nome_brigada'],
        category: b['categoria_brigada'],
        filterString: b['nome_brigada'] + b['placa'] + b['categoria_brigada'],
        id_brigada: b['id_brigada'],
        plate: b['placa']
      })
    }

    this.brigades.sort((a, b) => a.name.localeCompare(b.name));

    if (data.row === null){
      this.filterBrigades('');
    } else {
      this.filterBrigades(data.row['nome_brigada']);
    }

    let id_acionamento = `${data.id_report}_${data.len_acionamentos}`

    this.initForm(data.row, id_acionamento);
  }

  filterBrigades(nome_brigada) {
    this.filteredBrigades = this.brigadeCtrl.valueChanges.pipe(
      startWith(nome_brigada),
      map(brigade => (brigade ? this._filterBrigades(brigade) : this.brigades.slice())),
      );
    }
    
  private _filterBrigades(value: string): Brigade[] {
    const filterValue = value.toLowerCase();
    return this.brigades.filter(brigade => brigade.filterString.toLowerCase().includes(filterValue)).sort((a, b) => a.name.localeCompare(b.name));
  }
  
  initForm(row, id_acionamento){
    let datetime_now = formatDate(new Date(), 'yyyy-MM-ddTHH:mm', 'en-US');
    let dt_midnight = this.setMidnight();

    let dt_validators = [
      this.dtValidators.dtAcionamento(),
      this.dtValidators.dtCombate(),
      this.dtValidators.dtRescaldo(),
      this.dtValidators.dtChegadaCombate(),
      this.dtValidators.dtChegadaRescaldo(),
      this.dtValidators.dtFuturo(dt_midnight)
    ]
    
    if (row === null){
      this.brigadeForm = this.fb.group({
        nome_brigada: this.brigadeCtrl,
        contagem_equipe:['1',[Validators.required, Validators.min(1)]],
        dt_acionamento:[`${datetime_now}`,[Validators.required]],
        dt_chegada:[''],
        combate: false,
        dt_inicio_combate:[''],
        dt_fim_combate:[''],
        rescaldo: false,
        dt_inicio_rescaldo:[''],
        dt_fim_rescaldo:[''],
        id_acionamento:[id_acionamento],
        id_brigada: ['']
      },{
        validators: dt_validators,
      });
    } else {
      if (row['dt_inicio_combate'] !== null && row['dt_inicio_combate'] !== ''){
        this.combateChecked = true;
      };
      if (row['dt_inicio_rescaldo'] !== null && row['dt_inicio_rescaldo'] !== ''){
        this.rescaldoChecked = true;
      };
      this.brigadeForm = this.fb.group({
        nome_brigada: [row['nome_brigada'],[Validators.required]],
        contagem_equipe:[row['contagem_equipe'],[Validators.required, Validators.min(1)]],
        dt_acionamento:[row['dt_acionamento'],[Validators.required]],
        dt_chegada:[row['dt_chegada']],
        combate: this.combateChecked,
        dt_inicio_combate:[row['dt_inicio_combate']],
        dt_fim_combate:[row['dt_fim_combate']],
        rescaldo: this.rescaldoChecked,
        dt_inicio_rescaldo: [row['dt_inicio_rescaldo']],
        dt_fim_rescaldo: [row['dt_fim_rescaldo']],
        id_acionamento: [row['id_acionamento']],
        id_brigada: [row['id_brigada']]
      },{
        validators: dt_validators,
      });
      console.log('autocomplete: ', this.autoComplete)
      this.autoComplete.closePanel();
    }
  }

解决方案

问题根源是组件构造函数执行时视图尚未初始化,@ViewChild无法获取到实例;同时编辑场景下startWith(nome_brigada)触发valueChanges,导致面板自动展开。按以下步骤修复:

  1. 修正@ViewChild获取方式
    对应HTML中input的#autoComplete模板变量,指定读取MatAutocompleteTrigger:

    @ViewChild('autoComplete', { read: MatAutocompleteTrigger }) 
    autoComplete: MatAutocompleteTrigger;
    
  2. 利用AfterViewInit生命周期钩子
    实现AfterViewInit接口,将视图相关操作移到ngAfterViewInit中执行(确保视图初始化完成):

    import { AfterViewInit } from '@angular/core';
    
    export class SelectBrigadeDialog implements AfterViewInit {
      // ...其他代码
    
      ngAfterViewInit(): void {
        if (this.data.row === null){
          this.filterBrigades('');
        } else {
          this.filterBrigades(this.data.row['nome_brigada']);
        }
    
        let id_acionamento = `${this.data.id_report}_${this.data.len_acionamentos}`;
        this.initForm(this.data.row, id_acionamento);
    
        // 编辑场景下延迟关闭面板,确保valueChanges触发完成
        if (this.data.row) {
          setTimeout(() => {
            this.autoComplete?.closePanel();
          }, 0);
        }
      }
    }
    
  3. 调整构造函数逻辑
    构造函数中只保留数据初始化操作,移除视图相关调用:

    constructor(
      @Inject(MAT_DIALOG_DATA)
      public data: any,
      public fb: FormBuilder,
      private dtValidators: DatetimeValidators,
      private amplitude: AmplitudeService
    ) {
      this.combateChecked = false;
      this.rescaldoChecked = false;
    
      for (let b of data.brigadas){
        this.brigades.push({
          type: b['tipo_brigada'],
          typeIcon: brigadeIconMap[b['id_tipo_brigada']],
          name: b['nome_brigada'],
          category: b['categoria_brigada'],
          filterString: b['nome_brigada'] + b['placa'] + b['categoria_brigada'],
          id_brigada: b['id_brigada'],
          plate: b['placa']
        })
      }
    
      this.brigades.sort((a, b) => a.name.localeCompare(b.name));
    }
    

修改后,编辑场景下表单初始化完成后面板会自动关闭,且不会出现undefined报错。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:40:30