MultiSelect (server-side filtering) removes previously selected items — how to preserve selected values?

When using <ejs-multiselect> with server-side filtering and CheckBox mode, previously selected items disappear from the dropdown list and tag area shows only the keys (or undefined) after I fetch filtered data. I want selected items to remain visible and have their text shown even if they are not part of the current filter result.

If my approach (merge previous selections into fetched results) is the correct pattern, why might tags still appear as keys/undefined? What am I missing?


following you can see minimal reproduction.

Environment

  • Angular: "^18.2.13"

  • Syncfusion package: @syncfusion/ej2-angular-dropdowns ("^30.1.37")

  • Mode: CheckBox + allowFiltering=true + server-side filtering (using (filtering) event)

multiselect.component.html

<ejs-multiselect

  #comboComponent
  id="countryCombo"
  [fields]="fields"
  [dataSource]="dataCollection"
  [allowFiltering]="true"
  [mode]="'CheckBox'"
  [value]="selectedValues"
  (filtering)="comboFiltering($event)">
</ejs-multiselect>

 multiselect.component.ts

@Component({
  selector: 'app-multi-select',
  templateUrl: './multi-select.component.html',
  styleUrls: ['./multi-select.component.scss'],
  providers: [CheckBoxSelectionService]
})
export class MultiSelectComponent implements OnInit {

@ViewChild('comboComponent') comboComponent!: MultiSelectComponent;
fields = { text: 'textValue', value: 'key' };
dataCollection = [];
initialCollection = [];
selectedValues = ['CA','FR']; // preselected


public comboFiltering(e: FilteringEventArgs) {
  e.cancel = true;
  e.preventDefaultAction = true;
  this.setDebounce(e);
}


public setDebounce = debounce((e: FilteringEventArgs) => {
  this.onFiltering(e);
}, 400);


async onFiltering(e: FilteringEventArgs) {
  if (e.text && e.text.length >= 1) {
    await this.readCombo(e.text);
    this.comboComponent.showPopup();
  } else {
    this.dataCollection = this.initialCollection;
  }
}


async readCombo(searchTerm = '') {
  // call service that returns items matching searchTerm
  const data = await this.myService.getCombo(searchTerm).toPromise();


  // map server result to { key, textValue }
  const fetched = data.map(item => ({ key: item.id, textValue: item.name }));


  // attempted merge: keep selected items visible even if not in fetched
  const selectedKeys = this.comboComponent?.value || [];
  const previouslySelected = this.initialCollection.length
    ? this.initialCollection.filter(x => selectedKeys.includes(x.key))
    : []; // fallback to other source


  const merged = [
    ...previouslySelected.filter(sel => !fetched.some(f => f.key === sel.key)),
    ...fetched
  ];


  this.dataCollection = merged;
}
}








1 Reply

MR Mallesh Ravi Chandran Syncfusion Team October 9, 2025 03:39 AM UTC

Based on the provided scenario and validation, the described behavior is expected when using the multiselect component with filtering and CheckBox mode. By default, the popup list displays only the filtered items , and previously selected items that are not part of the current filtered result will not appear in the dropdown or tag area unless explicitly handled.
To preserve and display selected values (including their text) even when they are not part of the current filter result, it is necessary to customize the filtering logic. The current approach of merging previously selected items into the filtered result is valid. However, the reported where tags appear as only keys or undefined typically occurs due to one or more of the following reasons:
Possible Causes:
  1. Missing Text Mapping: If the merged previouslySelected items do not have the correct textValue property, the component cannot resolve the display text, resulting in undefined or just the key being shown.
  2. Data Binding Not Triggered: After updating the dataSource, it is important to call the dataBind() method on the component to ensure the UI reflects the updated data.

To ensure selected items remain visible and their text is shown correctly, update the dataSource of the component directly and trigger data binding after merging:

 async onFiltering(e: FilteringEventArgs) {
    if (e.text && e.text.length >= 1) {
      await this.readCombo(e.text);
    } else {
      this.comboComponent.dataSource = this.initialCollection;
      this.comboComponent.dataBind();
    }
  }

  async readCombo(searchTerm: string = '') {
    const data = await this.myService.getCombo(searchTerm).toPromise();
    const fetched = data.map((item) => ({
      key: item.id,
      textValue: item.name,
    }));

    const selectedKeys = this.comboComponent?.value || [];
    const previouslySelected = this.initialCollection.filter((x) =>
      (selectedKeys as any).includes(x.key)
    );

    const merged = [
      ...previouslySelected.filter(
        (sel) => !fetched.some((f) => f.key === sel.key)
      ),
      ...fetched,
    ];

    this.comboComponent.dataSource = merged;
    this.comboComponent.dataBind();
  }



This ensures that the component recognizes the updated data and re-renders . If any issues persist after applying the suggested changes, it is kindly requested to either share a runnable sample that replicates the issue or reproduce the reported scenario in the provided sample . This will help in thoroughly analyzing the behavior and offering more accurate and effective assistance.

Loader.
Up arrow icon