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

Angular 13中REST API填充Autocomplete选项异常:点击无显示输入后出现

Angular Material Autocomplete 点击输入框不显示选项的修复方案

问题描述

使用Angular Material的Autocomplete组件加载REST API中的选项时,点击输入框选项列表为空,只有输入内容后才会显示选项。通过getData()函数从API获取数据填充options数组,推测问题源于options是普通数组,而filteredOptions作为Observable的创建时机早于数据填充,导致初始点击时无数据。预期点击输入框即可看到完整选项列表。

原代码

HTML:

<form class="example-form">
  <mat-form-field class="example-full-width" appearance="fill">
    <mat-label>Number</mat-label>
    <input type="text"
           placeholder="Pick one"
           aria-label="Number"
           matInput
           [formControl]="myControl"
           [matAutocomplete]="auto">
    <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
      <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
        {{option}}
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

TypeScript:

import { Component, OnInit} from '@angular/core';
import { FormControl} from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith} from 'rxjs/operators';
import { AuthenticationService, Products_Header_VW } from '...';

@Component({
selector: 'app-test-second',
templateUrl: './test-second.component.html',
styleUrls: ['./test-second.component.scss']
})
export class TestSecondComponent implements OnInit {
myControl = new FormControl('');
options: string[] = [];
filteredOptions!: Observable<any>;

constructor(private auth: AuthenticationService) {
    this.getData().subscribe(x => {
      this.options = x;
    });
}

ngOnInit() {
    this.filteredOptions = this.myControl.valueChanges.pipe(
    startWith(''),
    map(value => this._filter(value || '')),
    );
}

private _filter(value: string): string[] {
    const filterValue = value;

    return this.options.filter(option => option.includes(filterValue));
}

//**** 获取所有产品数据并转换为字符串数组 ****
private getData(): Observable<string[]> {
    return this.auth.getallproductheadersvw()
    .pipe(
        map(
        x => x.data.map((y:Products_Header_VW) => y.Product_Title_Persian)
        )
    )
}
}

问题原因

  1. filteredOptions在ngOnInit中初始化,仅监听myControl.valueChanges流,初始startWith('')触发过滤时,options还是空数组(API请求异步未返回)。
  2. 当API数据返回更新options数组时,filteredOptions不会重新触发计算,因为它不监听options的变化。

修复方案

方案一:用BehaviorSubject管理选项数据(推荐)

通过BehaviorSubject包装选项数据,让filteredOptions同时监听表单值变化和选项数据的更新,确保数据同步。

修改后的TypeScript代码:

import { Component, OnInit} from '@angular/core';
import { FormControl} from '@angular/forms';
import { Observable, BehaviorSubject, combineLatest} from 'rxjs';
import { map, startWith} from 'rxjs/operators';
import { AuthenticationService, Products_Header_VW } from '...';

@Component({
selector: 'app-test-second',
templateUrl: './test-second.component.html',
styleUrls: ['./test-second.component.scss']
})
export class TestSecondComponent implements OnInit {
myControl = new FormControl('');
// 用BehaviorSubject存储选项数据,初始为空数组
private optionsSubject = new BehaviorSubject<string[]>([]);
// 对外暴露Observable,避免直接修改内部数据
options$ = this.optionsSubject.asObservable();
filteredOptions!: Observable<string[]>;

constructor(private auth: AuthenticationService) {}

ngOnInit() {
    // 合并表单值变化和选项数据的流,任一变化都重新过滤
    this.filteredOptions = combineLatest([
      this.myControl.valueChanges.pipe(startWith('')),
      this.options$
    ]).pipe(
      map(([value, options]) => this._filter(value || '', options))
    );

    // 请求API数据并更新BehaviorSubject
    this.getData().subscribe(x => {
      this.optionsSubject.next(x);
    });
}

// 调整过滤函数,接收当前的选项列表参数
private _filter(value: string, options: string[]): string[] {
    const filterValue = value;
    return options.filter(option => option.includes(filterValue));
}

private getData(): Observable<string[]> {
    return this.auth.getallproductheadersvw()
    .pipe(
        map(x => x.data.map((y:Products_Header_VW) => y.Product_Title_Persian))
    );
}
}

方案二:数据返回后初始化filteredOptions

如果不需要后续更新选项数据,可在API数据返回后再创建filteredOptions,确保初始过滤时已有完整数据:

修改后的TypeScript代码:

import { Component, OnInit} from '@angular/core';
import { FormControl} from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith} from 'rxjs/operators';
import { AuthenticationService, Products_Header_VW } from '...';

@Component({
selector: 'app-test-second',
templateUrl: './test-second.component.html',
styleUrls: ['./test-second.component.scss']
})
export class TestSecondComponent implements OnInit {
myControl = new FormControl('');
options: string[] = [];
filteredOptions!: Observable<any>;

constructor(private auth: AuthenticationService) {}

ngOnInit() {
    this.getData().subscribe(x => {
      this.options = x;
      // 数据返回后再初始化filteredOptions
      this.filteredOptions = this.myControl.valueChanges.pipe(
        startWith(''),
        map(value => this._filter(value || '')),
      );
    });
}

private _filter(value: string): string[] {
    const filterValue = value;
    return this.options.filter(option => option.includes(filterValue));
}

private getData(): Observable<string[]> {
    return this.auth.getallproductheadersvw()
    .pipe(
        map(x => x.data.map((y:Products_Header_VW) => y.Product_Title_Persian))
    );
}
}

效果说明

两种方案都能实现:点击输入框时,filteredOptions会基于完整的API返回数据执行过滤(空值匹配所有选项),从而显示全部选项列表。方案一更适合需要动态更新选项数据的场景,方案二更简洁适用于一次性加载数据的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:49:14