---
name: syncfusion-diagram-id-mismatch-overlay
description: |
  Debug and fix SyncFusion EJ2 diagram overlays (heatmaps, annotations, badges) that fail to render
  because database node IDs don't match current diagram node IDs. Use when: (1) overlay shows "0 data
  points" but API returns valid data, (2) heatmap/annotation data exists but nothing renders on
  diagram elements, (3) diagram was modified (racks duplicated, elements rebuilt) after initial
  database records were created, (4) JavaScript console shows DOM positions found but no matching
  zones. Covers multi-strategy ID matching pattern: exact match and forward prefix (SAFE), with
  warnings against reverse-prefix matching (DANGEROUS). Includes duplicate prevention.
author: Claude Code
version: 1.1.0
date: 2026-01-26
---

# SyncFusion Diagram ID Mismatch - Overlay Rendering Failure

## Problem

When rendering overlays (heatmaps, annotations, badges) on a SyncFusion EJ2 Diagram, the overlay
fails to appear despite having valid data. The JavaScript matches database records to diagram
nodes by ID, but the IDs no longer match because the diagram was modified after the database
records were created.

## Context / Trigger Conditions

- Overlay shows "0 data points" or "no data to render" in console
- API returns valid data with node IDs and values
- Diagram elements are visible and rendering correctly
- Console shows "DOM positions found: X" but heatmap/overlay is empty
- The diagram was modified after initial setup (elements duplicated, diagram rebuilt)
- Database records reference `syncfusionNodeId` values that don't exist in current diagram

**Key Symptom**: Data exists, diagram exists, but the overlay JavaScript can't connect them.

## Root Cause

### SyncFusion ID Suffix Appending

When you **duplicate** a node in SyncFusion EJ2 Diagram, it creates a new ID by appending
a random suffix to the original ID:

```
Original:   rack_1769202843447_1ydwampwi
Duplicate:  rack_1769202843447_1ydwampwiKRCPl
Duplicate2: rack_1769202843447_1ydwampwiwefISTEg0X
```

If your database stores the original ID (`rack_1769202843447_1ydwampwi`) but the diagram
now only has the duplicated IDs, exact matching fails.

### Diagram Rebuild Without Database Sync

When a diagram is completely rebuilt (deleted and recreated), new nodes get entirely new IDs.
The database still references the old IDs that no longer exist anywhere in the diagram.

## Solution: Multi-Strategy ID Matching

Implement a matching function that tries multiple strategies in order:

```javascript
// Track which data items have been matched (prevent duplicates)
const matchedIds = new Set();

function findMatchingDataItems(diagramNodeId, dataItemsBySyncfusionId) {
    // Strategy 1: Exact match
    if (dataItemsBySyncfusionId[diagramNodeId]) {
        const items = dataItemsBySyncfusionId[diagramNodeId]
            .filter(item => !matchedIds.has(item.id) && item.value > 0);
        if (items.length > 0) return items;
    }

    // Strategy 2: Prefix match - data item's ID is prefix of diagram node ID
    // (handles duplicated nodes with appended suffixes)
    let bestMatch = null;
    let bestMatchLength = 0;

    for (const [syncId, items] of Object.entries(dataItemsBySyncfusionId)) {
        if (diagramNodeId.startsWith(syncId) && syncId.length > bestMatchLength) {
            const unmatchedItems = items.filter(item =>
                !matchedIds.has(item.id) && item.value > 0
            );
            if (unmatchedItems.length > 0) {
                bestMatch = unmatchedItems;
                bestMatchLength = syncId.length;
            }
        }
    }
    if (bestMatch) {
        console.log(`Prefix match: ${diagramNodeId} matched (prefix length ${bestMatchLength})`);
        return bestMatch;
    }

    // ⚠️ DANGEROUS - DO NOT USE Strategy 3: Reverse prefix matching
    // See "Why Reverse Prefix Matching is Dangerous" section below.
    // Reverse prefix (syncId.startsWith(diagramNodeId)) causes data to render
    // on WRONG nodes when duplicated racks share ID prefixes.

    return [];
}
```

### Key Pattern: Longest Prefix Wins

When multiple database IDs could match via prefix, choose the **longest matching prefix**
to avoid false positives. Example:

```
Diagram node: rack_1769202843447_1ydwampwiKRCPl

Database IDs:
- rack_1769202843447          (length 21) - matches but short
- rack_1769202843447_1ydwampwi (length 31) - matches and longer ✓

Use the longer match (31 chars) as it's more specific.
```

### Duplicate Prevention

Track matched items to prevent the same data from rendering on multiple diagram nodes:

```javascript
const matchedIds = new Set();

// After matching...
matchedItems.forEach(item => matchedIds.add(item.id));
```

Without this, if multiple diagram nodes match the same database record via prefix,
you'll render duplicate overlays.

### Fallback: Unmatched Data Rendering

For data items that can't match ANY diagram node, render them at their stored positions
as a fallback (useful for historical data from deleted elements):

```javascript
const unmatchedWithValues = allDataItems.filter(item =>
    !matchedIds.has(item.id) && item.value > 0
);

if (unmatchedWithValues.length > 0) {
    console.log(`${unmatchedWithValues.length} unmatched items - rendering at stored positions`);

    unmatchedWithValues.forEach(item => {
        // Transform stored positions to current viewport coordinates
        const screenX = item.positionX * zoom + horizontalOffset;
        const screenY = item.positionY * zoom + verticalOffset;

        renderOverlayAt(screenX, screenY, item.value);
    });
}
```

## Verification

1. Add console logging to show which matching strategy succeeded for each node
2. Count matched vs unmatched data items
3. Visual inspection: overlay should now appear on diagram elements
4. Check that no duplicate overlays appear (Set tracking working)

```javascript
console.log(`Matched ${matchedIds.size} items to diagram nodes`);
console.log(`Unmatched items with data: ${unmatchedWithValues.length}`);
```

## Example: Heatmap Implementation

```javascript
function renderHeatmap(zones, diagramInstance) {
    const matchedZoneIds = new Set();
    const heatmapData = [];

    // Build lookup by syncfusion ID
    const zonesBySyncfusionId = {};
    zones.forEach(zone => {
        if (!zonesBySyncfusionId[zone.rackSyncfusionId]) {
            zonesBySyncfusionId[zone.rackSyncfusionId] = [];
        }
        zonesBySyncfusionId[zone.rackSyncfusionId].push(zone);
    });

    // Match diagram nodes to zones
    diagramInstance.nodes.forEach(node => {
        if (!node.id?.startsWith('rack_')) return;

        const matchedZones = findMatchingZones(node.id, zonesBySyncfusionId, matchedZoneIds);

        // Mark as matched
        matchedZones.forEach(z => matchedZoneIds.add(z.socketId));

        // Aggregate values
        const totalValue = matchedZones.reduce((sum, z) => sum + (z.totalSales || 0), 0);

        if (totalValue > 0) {
            const domPos = getDomPosition(node.id);
            heatmapData.push({
                x: domPos.x,
                y: domPos.y,
                value: totalValue
            });
        }
    });

    // Handle unmatched zones with data
    const unmatchedZones = zones.filter(z =>
        !matchedZoneIds.has(z.socketId) && (z.totalSales || 0) > 0
    );
    // ... render at stored positions as fallback

    return heatmapData;
}
```

## ⚠️ Why Reverse Prefix Matching is DANGEROUS

**DO NOT** use reverse prefix matching (checking if `databaseId.startsWith(diagramNodeId)`).
This causes overlays to render on the WRONG diagram nodes.

### The Problem

When racks are duplicated in SyncFusion, the new ID is created by **appending** to the original:

```
Original "Girls Rack 5":  rack_1769202843447_1ydwampwiKRCPl
Duplicated "Guys Rack 5": rack_1769202843447_1ydwampwiKRCPlTo3eOUeeTP
                          ↑________________________↑
                          Guys Rack 5's ID STARTS WITH Girls Rack 5's ID!
```

### What Goes Wrong

With reverse prefix matching, when iterating through diagram nodes:
1. You find "Girls Rack 5" node with ID `rack_xxx_KRCPl`
2. You check database records - "Guys Rack 5" has ID `rack_xxx_KRCPlTo3eOUeeTP`
3. Reverse prefix check: `"rack_xxx_KRCPlTo3eOUeeTP".startsWith("rack_xxx_KRCPl")` → **TRUE!**
4. **BUG**: Guys Rack 5's sales data now renders at Girls Rack 5's position!

### The Real-World Result

The heatmap showed sales data on the GIRLS racks when the user had only assigned
categories to the GUYS racks. This is because the Guys racks were created by
duplicating Girls racks, and reverse prefix matching incorrectly associated
the Guys rack data with the Girls rack positions.

### Safe Strategies Only

| Strategy | Direction | Safe? | Use Case |
|----------|-----------|-------|----------|
| Exact match | `diagramId === dbId` | ✅ SAFE | Perfect match |
| Forward prefix | `diagramId.startsWith(dbId)` | ✅ SAFE | Diagram node was duplicated FROM db record |
| Reverse prefix | `dbId.startsWith(diagramId)` | ❌ DANGEROUS | Never use this |

**Forward prefix is safe** because it finds cases where the database has the original
ID and the diagram node was created by duplicating it (appending suffix).

**Reverse prefix is dangerous** because multiple diagram nodes' IDs can share
the same prefix when they were duplicated from a common ancestor.

## Notes

1. **Position-based matching** as a last resort: Increase tolerance from 50px to 100px+
   when diagrams may have been modified

2. **This is a data synchronization problem**: The ideal fix is to keep database IDs
   in sync when diagrams are modified. This skill provides a runtime workaround.

3. **Consider a sync mechanism**: When saving a diagram, update database records to
   match current node IDs

4. **Related skill**: `syncfusion-diagram-connector-corruption` covers similar issues
   with connector elements

5. **Test with modified diagrams**: Always test overlay rendering after duplicating
   elements or rebuilding the diagram

## References

- [SyncFusion EJ2 Diagram Nodes API](https://ej2.syncfusion.com/documentation/api/diagram/node/)
- [SyncFusion Diagram Save/Load](https://ej2.syncfusion.com/documentation/diagram/serialization/)
