---
name: syncfusion-grid-search-dataSource-race
description: |
  Fix Syncfusion EJ2 Grid showing "No records to display" when switching from Grid.search()
  to manual dataSource assignment. Use when: (1) grid.search('term') is active and you then
  set grid.dataSource = filteredArray, (2) the filtered array has correct records but Grid
  shows 0 items, (3) transitioning between search modes (e.g. single-term to comma-separated
  search). Root cause: Grid.search() applies an internal filter that persists even when
  dataSource is reassigned, further filtering the new data against the old search string.
author: Claude Code
version: 1.0.0
date: 2026-03-27
---

# Syncfusion EJ2 Grid: search() + dataSource Race Condition

## Problem
When Syncfusion EJ2 Grid has an active `grid.search('term')` filter, assigning a new
`grid.dataSource = filteredArray` does NOT clear the internal search filter. The Grid
applies BOTH filters — the internal search filter AND the new dataSource — resulting in
"No records to display" even though the dataSource contains valid records.

## Context / Trigger Conditions
- Grid has an active `grid.search(searchTerm)` filter displaying results
- Code then sets `grid.dataSource = someFilteredArray` directly
- The filtered array is correct but Grid shows 0 items
- Common scenario: transitioning from Grid's built-in search to a custom filter mode
  (e.g., comma-separated multi-search that manually filters and assigns dataSource)

## Solution

### Step 1: Clear the Grid's internal search filter first
```javascript
// BEFORE setting dataSource, clear the search filter
self.grid.search('');

// THEN set the new dataSource
self.grid.dataSource = matchedRecords;
```

### Step 2: Handle async timing
`grid.search('')` triggers an async re-render. If you need to do work after the
dataSource change (like auto-selecting rows), use the `dataBound` event with a
flag pattern instead of `setTimeout`:

```javascript
// Set a flag BEFORE changing dataSource
self._pendingAction = true;

// Change dataSource (triggers async dataBound)
self.grid.dataSource = matchedRecords;

// In the Grid's dataBound handler:
dataBound: function(args) {
    if (self._pendingAction) {
        self._pendingAction = false;
        // Use setTimeout(fn, 0) to ensure Grid's internal render cycle completes
        setTimeout(function() {
            self.grid.selectRows([0, 1, 2]); // or whatever post-render action
        }, 0);
    }
}
```

### Step 3: Guard against stale async operations
If the user types quickly, multiple async operations can overlap. Use a version
token to ensure only the latest operation takes effect:

```javascript
var version = ++self._searchVersion;

// In the async callback:
setTimeout(function() {
    if (version !== self._searchVersion) return; // stale, skip
    self.grid.dataSource = matchedRecords;
}, 50);
```

## Verification
1. Perform a single-term search (e.g., "BIN-00") — Grid filters normally
2. Switch to custom dataSource assignment — Grid should show the new records
3. Grid should NOT show "No records to display"

## Example
In BuyerKiosk backstock, transitioning from single-term search to comma-separated
search caused "No records to display":

**Before (broken):**
```javascript
// Single-term search active: grid.search('BIN-00') filtering 9 bins
// User types comma search: "BIN-001, BIN-010"
self.grid.dataSource = matchedRecords; // 2 records
// Grid shows 0 items! Internal search filter still active,
// filtering "BIN-001, BIN-010" against the 2 records
```

**After (fixed):**
```javascript
self.grid.search('');  // Clear internal search filter
self.clearSelection();
setTimeout(function() {
    self._pendingCommaSelectCount = matchedRecords.length;
    self.grid.dataSource = matchedRecords; // Now shows 2 records correctly
}, 50);
```

## Notes
- This applies to ANY scenario mixing `grid.search()` with manual `dataSource` assignment
- `grid.search('')` with empty string clears the internal filter
- The 50ms delay between `search('')` and `dataSource` assignment ensures the Grid's
  async processing from `search('')` completes before the new data is set
- `dataBound` fires multiple times during this sequence (once for search clear, once
  for dataSource change) — use flags carefully to target the right event
- `selectRows()` called during `dataBound` may not stick — wrap in `setTimeout(fn, 0)`
  to defer until after Grid's internal render cycle completes
