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

Angular Material自动补全多组件共用选项时按isAvailable条件过滤实现

修复方案

现有代码的问题

  1. 公共服务planets的订阅回调中,过滤逻辑没有返回值,也没有同步到自动补全的过滤流,导致数据源更新后视图不刷新
  2. filteredPlanet仅在构造函数初始化一次,后续全局planet数组更新时不会重新触发过滤
  3. 同时使用[formControl]和[(ngModel)]是Angular已废弃的混合用法,会触发控制台警告
  4. 直接过滤掉不可用选项的逻辑,如果需求是「显示但禁用」已选选项则不符合交互要求

调整后的TS代码

import { Component, OnInit, ViewEncapsulation, OnDestroy } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable, combineLatest, Subscription, take } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { CommonService } from '../common.service';

// 定义Planet类型方便类型校验
interface Planet {
  name: string;
  distance: number;
  isAvailable: boolean;
}

@Component({
  selector: 'falcone-traveldetails',
  templateUrl: './traveldetails.component.html',
  styleUrls: ['./traveldetails.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class TraveldetailsComponent implements OnInit, OnDestroy {
  planetControl = new FormControl('');
  filteredPlanet: Observable<Planet[]>;
  previousPlanet: Planet | null = null;
  vehicles: any;
  private subscription = new Subscription();

  constructor(private commonService: CommonService) {
    // 合并输入框值变化、全局planet数据源变化两个流,任意更新就重新过滤
    this.filteredPlanet = combineLatest([
      this.planetControl.valueChanges.pipe(startWith('')),
      this.commonService.planets
    ]).pipe(
      map(([inputValue, planets]) => {
        const filterKeyword = (typeof inputValue === 'string' ? inputValue : inputValue?.name)?.toLowerCase() || '';
        return planets.filter(planet => 
          planet.name.toLowerCase().includes(filterKeyword)
          // 如果需要直接隐藏不可用选项,放开下面这行注释即可
          // && planet.isAvailable === true
        );
      })
    );

    const vehicleSub = this.commonService.vehicles.subscribe(data => {
      this.vehicles = data;
    });
    this.subscription.add(vehicleSub);
  }

  ngOnInit(): void {
  }

  ngOnDestroy(): void {
    // 销毁时取消订阅避免内存泄漏
    this.subscription.unsubscribe();
  }

  getPlanetName(planet: Planet): string | undefined {
    return planet ? planet.name : undefined;
  }

  onPlanetChanged(event: any): void {
    const selected = event.option.value as Planet;
    // 取全局最新的planet数组操作,避免本地和全局数据不一致
    this.commonService.planets.pipe(take(1)).subscribe(globalPlanets => {
      const updatedPlanets = globalPlanets.map(planet => {
        // 新选中的设为不可用
        if(planet.name === selected.name) return {...planet, isAvailable: false};
        // 之前选中的恢复可用
        if(this.previousPlanet?.name === planet.name) return {...planet, isAvailable: true};
        return planet;
      });
      this.commonService.updatePlanetsList(updatedPlanets);
      this.previousPlanet = selected;
    });
  }

}

调整后的HTML代码

如果需要实现「显示但禁用不可用选项」的效果,给mat-option加[disabled]属性即可:

<mat-form-field>
  <input type="text" placeholder="Choose planet" matInput 
         [matAutocomplete]="planetAuto" 
         [formControl]="planetControl"/>
  <mat-autocomplete #planetAuto="matAutocomplete" 
                    [displayWith]="getPlanetName" 
                    (optionSelected)="onPlanetChanged($event)">
    <mat-option *ngFor="let planet of filteredPlanet | async" 
                [value]="planet"
                [disabled]="!planet.isAvailable">
      {{planet.name}}
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

注意事项

  • 公共服务中的planets需要声明为BehaviorSubject类型,才能保证所有组件都能拿到最新的数组状态
  • 操作数组时建议用展开运算符生成新数组,避免引用类型导致的意外修改问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 13:36:06