1

I have a bug in my Angular code where the selection state of a multiple selection mat-select component is lost when filtering the options.

In my HTML template for the component, I have:

    <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>

In my component's TypeScript file, I have:

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',
  ];
}

And I'm using a custom filter pipe to filter the options based on user input:

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

The issue I'm having is that when I select an option and then filter the options, the selection state of the previously selected option is lost. For example, if I select "Mushroom" and then search for "Onion", "Mushroom" becomes unselected.

What am I doing wrong? How can I preserve the selection state of the options while filtering them?

StackBlitz Demo

Alex
  • 157
  • 1
  • 10

0 Answers0