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

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);
        });
    }
}

问题原因

  1. FormControl初始化错误:多选模式下,FormControl的初始值应为空数组[],而非空字符串''。空字符串不符合多选值的数组类型,会导致组件内部状态处理异常。
  2. 筛选后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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:35:45