Angular 18中ngx-mat-select-search除ALL外选项无法点击求助
Angular 18中ngx-mat-select-search多选下拉搜索后点击选项无响应问题
在Angular 18项目中使用ngx-mat-select-search为mat-select多选下拉菜单实现搜索功能时,遇到以下问题:搜索'ALL'后可正常选中该选项,但点击菜单中的其他模块选项时完全无响应。
问题演示

现有代码
HTML代码
<mat-form-field appearance="fill" class="matformfield"> <mat-label class="matlabel" *ngIf="!selectedModules.length">Select Modules</mat-label> <mat-select #drop multiple [(ngModel)]="selectedModules"> <ngx-mat-select-search [formControl]="control" ></ngx-mat-select-search> <mat-select-trigger *ngIf="drop.value && drop.value.length"> {{ drop.value.length === modules.length ? 'ALL' : drop.value}} </mat-select-trigger> <mat-option class="matoption" *ngFor="let module of filteredRecords | async" [value]="module" [disabled]="isAllSelected() && module !== 'ALL'" (click)="onClick(drop.options, module === 'ALL')">{{ module }}</mat-option> </mat-select> </mat-form-field>
TypeScript代码
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { FormControl } from '@angular/forms'; import { MatSelect, MatOption } from '@angular/material/select'; import { ReplaySubject, Subject, takeUntil, take, QueryList } from 'rxjs'; export class ModuleMenuComponent implements OnInit, OnDestroy { public control: FormControl = new FormControl(); private _onDestroy = new Subject<void>(); public filteredRecords: ReplaySubject<any[]> = new ReplaySubject<any[]>(1); @ViewChild('Select') select: MatSelect; modules: any[] = [ 'ALL', 'lorem', 'ipsum', 'domor', 'sit', 'amet' ]; selectedModules: string[] = []; ngOnInit() { this.setInitialValue(); this.control.setValue(''); this.filteredRecords.next(this.modules.slice()); this.control.valueChanges .pipe(takeUntil(this._onDestroy)) .subscribe(() => { this.filterModulesMulti(); }); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } private setInitialValue() { this.filteredRecords .pipe(take(1), takeUntil(this._onDestroy)) .subscribe(() => { this.select.compareWith = (a: any, b: any) => a === b; }); } private filterModulesMulti() { if (!this.modules) { return; } let search = this.control.value; if (!search) { this.filteredRecords.next(this.modules.slice()); return; } else { search = search.toLowerCase(); } if (search.length >= 2) { this.filteredRecords.next( this.modules.filter(module => module.toLowerCase().indexOf(search) > -1) ); } } clearFields() { this.selectedModules = []; } isAllSelected(): boolean { return this.selectedModules.includes('ALL'); } onClick(options:QueryList<MatOption>, isFirst:boolean=false) { if (isFirst) { const el = options.first; options.forEach((x) => { x[el.selected ? 'select' : 'deselect'](); }); if (el.selected) { this.selectedModules = this.modules.slice(); } else { this.selectedModules = []; } } else { const selected = options.filter((x, index) => index && x.selected); if (selected.length == this.modules.length - 1) { options.first.select(); this.selectedModules = this.modules.slice(); } else { options.first.deselect(); this.selectedModules = selected.map(x => x.value); } } } }
解决思路
1. 修复ViewChild选择器错误
HTML中mat-select的模板变量是#drop,但TS中@ViewChild('Select')的选择器不匹配,导致this.select始终为undefined,compareWith逻辑无法生效。将TS中的ViewChild改为:
@ViewChild('drop') select: MatSelect;
2. 修正过滤逻辑的漏洞
当前过滤逻辑仅在搜索字符长度≥2时才执行过滤,输入单个字符时列表不会更新,导致选项状态异常。移除长度判断,确保任何搜索输入都能触发过滤:
private filterModulesMulti() { if (!this.modules) return; let search = this.control.value?.toLowerCase() || ''; this.filteredRecords.next( this.modules.filter(module => module.toLowerCase().includes(search)) ); }
3. 移除手动点击事件,通过ngModel处理全选逻辑
手动操作MatOption的select()/deselect()方法会与[(ngModel)]的双向绑定冲突,尤其是在过滤后选项列表变化时。改用selectedModules的 setter 处理全选逻辑:
private _selectedModules: string[] = []; get selectedModules(): string[] { return this._selectedModules; } set selectedModules(value: string[]) { if (!value) { this._selectedModules = []; return; } // 处理全选逻辑 if (value.includes('ALL')) { this._selectedModules = [...this.modules]; } else { // 若选中所有非ALL选项,自动触发全选 if (value.length === this.modules.length - 1) { this._selectedModules = [...this.modules]; } else { // 移除ALL,避免手动选中其他选项时残留ALL this._selectedModules = value.filter(item => item !== 'ALL'); } } }
同时修改HTML,移除(click)事件和[disabled]属性:
<mat-option class="matoption" *ngFor="let module of filteredRecords | async" [value]="module"> {{ module }} </mat-option>
4. 确保组件生命周期正确实现
将初始化逻辑移至ngOnInit而非构造函数,并在ngOnDestroy中销毁订阅,避免内存泄漏(已在修正后的TS代码中体现)。
内容的提问来源于stack exchange,提问作者Rohan Buch
相关产品推荐
相关产品推荐

