Angular Material自动补全多组件共用选项时按isAvailable条件过滤实现
修复方案
现有代码的问题
- 公共服务
planets的订阅回调中,过滤逻辑没有返回值,也没有同步到自动补全的过滤流,导致数据源更新后视图不刷新 filteredPlanet仅在构造函数初始化一次,后续全局planet数组更新时不会重新触发过滤- 同时使用
[formControl]和[(ngModel)]是Angular已废弃的混合用法,会触发控制台警告 - 直接过滤掉不可用选项的逻辑,如果需求是「显示但禁用」已选选项则不符合交互要求
调整后的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
相关产品推荐
相关产品推荐

