Angular多选mat-select筛选时选中状态丢失问题求助
Angular多选mat-select筛选时丢失选中状态的问题
问题现象
多选mat-select组件在筛选选项时会丢失选中状态:选中一个选项(如"Mushroom")后,搜索其他选项(如"Onion"),之前选中的选项会失去选中状态。
相关代码
组件HTML模板
<mat-form-field appearance="fill"> <mat-label>Toppings</mat-label> <mat-select [formControl]="toppings" multiple> <input placeholder="Search..." style="padding: 12px; border-bottom: 1px solid #eee" [(ngModel)]="filteredInput" matInput /> <mat-option *ngFor="let topping of toppingList | filter : filteredInput" [value]="topping" >{{topping}}</mat-option > </mat-select> </mat-form-field>
组件TypeScript代码
import { Component } from '@angular/core'; import { FormControl } from '@angular/forms'; @Component({ selector: 'select-multiple-example', templateUrl: 'select-multiple-example.html', }) export class SelectMultipleExample { toppings = new FormControl(''); filteredInput = ''; toppingList: string[] = [ 'Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato', ]; }
自定义FilterPipe代码
import { Pipe, PipeTransform, Injectable } from '@angular/core'; @Pipe({ name: 'filter', pure: false, }) @Injectable() export class FilterPipe implements PipeTransform { /** * @param items object from array * @param term term's search * @param excludes array of strings which will ignored during search */ transform(items: any, term: string, excludes: any = []): any { if (!term || !items) return items; return FilterPipe.filter(items, term, excludes); } /** * * @param items List of items to filter * @param term a string term to compare with every property of the list * @param excludes List of keys which will be ignored during search * */ static filter( items: Array<{ [key: string]: any }>, term: string, excludes: any ): Array<{ [key: string]: any }> { const toCompare = term.toLowerCase(); function checkInside(item: any, term: string) { if ( typeof item === 'string' && item.toString().toLowerCase().includes(toCompare) ) { return true; } for (let property in item) { if ( item[property] === null || item[property] == undefined || excludes.includes(property) ) { continue; } if (typeof item[property] === 'object') { if (checkInside(item[property], term)) { return true; } } else if ( item[property].toString().toLowerCase().includes(toCompare) ) { return true; } } return false; } return items.filter(function (item) { return checkInside(item, term); }); } }
问题原因
- FormControl初始化错误:多选模式下,
FormControl的初始值应为空数组[],而非空字符串''。空字符串不符合多选值的数组类型,会导致组件内部状态处理异常。 - 筛选后DOM移除已选中项:当筛选关键词不匹配已选中选项时,该选项会被FilterPipe过滤掉并从DOM中移除。mat-select依赖DOM中的
mat-option元素渲染选中状态,元素移除后无法显示已选中状态(实际FormControl的值仍存在,但界面无法感知)。
解决方案
步骤1:修正FormControl初始化
将组件中的FormControl初始值改为空数组:
toppings = new FormControl([]);
步骤2:修改FilterPipe保留已选中项
更新FilterPipe,添加参数接收已选中的选项,确保已选中项即使不匹配筛选关键词也能保留在列表中:
import { Pipe, PipeTransform, Injectable } from '@angular/core'; @Pipe({ name: 'filter', pure: false, }) @Injectable() export class FilterPipe implements PipeTransform { transform(items: any, term: string, selectedItems: any[] = [], excludes: any = []): any { if (!term || !items) return items; return FilterPipe.filter(items, term, selectedItems, excludes); } static filter( items: Array<{ [key: string]: any }>, term: string, selectedItems: any[], excludes: any ): Array<{ [key: string]: any }> { const toCompare = term.toLowerCase(); function checkInside(item: any, term: string) { // 优先保留已选中的选项 if (selectedItems.includes(item)) { return true; } if ( typeof item === 'string' && item.toString().toLowerCase().includes(toCompare) ) { return true; } for (let property in item) { if ( item[property] === null || item[property] == undefined || excludes.includes(property) ) { continue; } if (typeof item[property] === 'object') { if (checkInside(item[property], term)) { return true; } } else if ( item[property].toString().toLowerCase().includes(toCompare) ) { return true; } } return false; } return items.filter(function (item) { return checkInside(item, term); }); } }
步骤3:更新HTML模板传递选中项
在调用FilterPipe时传入FormControl的选中值:
<mat-option *ngFor="let topping of toppingList | filter : filteredInput : toppings.value" [value]="topping" >{{topping}}</mat-option >
效果说明
修改后,已选中的选项会始终显示在筛选后的列表中,不会因不匹配关键词被移除,mat-select能正确渲染选中状态,同时不影响正常的筛选功能。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

