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?