Query Filter Loading Fix - Implementation

Problem Identified

From the diagnostic logs, the issue was NOT that query data wasn't being passed - it WAS being passed correctly. The problems were:

  1. Alpine Component Registration Failure - queryBuilderEdit is not defined error
  2. Incorrect Condition Logic - Checking if string equals literal template syntax {{ query_json|escapejs }}
  3. Timing Issue - Alpine.data() being registered AFTER HTMX inserted the DOM and Alpine scanned for x-data attributes

Root Cause

Looking at the console output:

text
Query JSON String from template: {"condition": "AND", "primary_model": "user", ...}
Query JSON is not available in context. Starting with empty groups.

The JSON WAS successfully rendered by Django templating, but the condition checking if queryJsonString === '{{ query_json|escapejs }}' was incorrect - this literal syntax would never match after templating.

Solution Implemented

1. Pre-Parse Template Data (Lines 20-47)

Instead of relying on parsing template variables inside Alpine methods, create a global QUERY_DATA object at the top level:

javascript
const QUERY_DATA = {
    queryId: parseInt('{{ object.id }}'),
    queryName: `{{ object.name }}`,
    selectedModel: `{{ object.content_type.model }}`,
    filtersByModel: {},
    queryJson: null
};

try {
    QUERY_DATA.filtersByModel = JSON.parse('{{ filters_by_model_json|escapejs }}');
} catch (e) {
    console.error('[SETUP] Error parsing filters_by_model_json:', e);
}

try {
    const queryJsonString = '{{ query_json|escapejs }}';
    if (queryJsonString && queryJsonString.trim().length > 0) {
        QUERY_DATA.queryJson = JSON.parse(queryJsonString);
    }
} catch (e) {
    console.error('[SETUP] Error parsing query_json:', e);
}

Benefits: - ✅ Parsing happens immediately when script loads, before Alpine processes DOM - ✅ All template variables are available to Alpine data object - ✅ Proper error handling with try/catch - ✅ No need to check for placeholder strings in methods

2. Updated Alpine Data Initialization (Lines 65-75)

Changed from inline template parsing to using pre-parsed global data:

javascript
window.Alpine.data('queryBuilderEdit', () => ({
    queryId: QUERY_DATA.queryId,
    queryName: QUERY_DATA.queryName,
    selectedModel: QUERY_DATA.selectedModel,
    filtersByModel: QUERY_DATA.filtersByModel,
    // ... rest of properties
}));

3. Simplified loadQueryData() Method (Lines 104-153)

Removed template variable parsing and incorrect condition checks:

javascript
loadQueryData() {
    // Load filters for the selected model
    if (this.selectedModel && this.filtersByModel[this.selectedModel]) {
        // ... load filters
    }

    // Use pre-parsed query data
    try {
        if (!QUERY_DATA.queryJson) {
            console.warn('[loadQueryData] No query data available...');
            if (this.groups.length === 0) {
                this.addGroup();
            }
            return;
        }
        
        const queryData = QUERY_DATA.queryJson;
        this.originalQueryData = JSON.parse(JSON.stringify(queryData)); // Deep copy
        
        if (queryData && queryData.groups && queryData.groups.length > 0) {
            // Reconstruct groups and rules
            this.groups = queryData.groups.map(group => ({
                id: Date.now() + Math.random(),
                condition: group.condition || 'AND',
                rules: group.rules.map(rule => ({
                    id: Date.now() + Math.random(),
                    field: rule.field,
                    operator: rule.operator,
                    value: Array.isArray(rule.value) ? rule.value[0] : rule.value,
                    value2: Array.isArray(rule.value) ? rule.value[1] : ''
                }))
            }));
            console.log('[loadQueryData] Loaded', this.groups.length, 'groups...');
        } else {
            if (this.groups.length === 0) {
                this.addGroup();
            }
        }
    } catch (error) {
        console.error('[loadQueryData] Error:', error);
        if (this.groups.length === 0) {
            this.addGroup();
        }
    }
}

4. Fixed Alpine Initialization (Lines 356-385)

Improved the initialization sequence:

javascript
// For fresh page loads where Alpine is already loaded
if (window.Alpine && window.Alpine.data) {
    console.log('[Init] Alpine already loaded, initializing now');
    initializeEditQueryBuilder();
}

// For HTMX-loaded partials
document.addEventListener('htmx:afterSettle', () => {
    console.log('[htmx:afterSettle] Event fired, initializing edit query builder');
    setTimeout(() => {
        initializeEditQueryBuilder();
    }, 100); // Small delay to ensure Alpine is fully ready
});

// Fallback for DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
    if (!window.Alpine) {
        console.log('[DOMContentLoaded] Alpine not yet available...');
        return;
    }
    console.log('[DOMContentLoaded] Initializing edit query builder');
    initializeEditQueryBuilder();
});

Benefits: - ✅ Handles both fresh page loads and HTMX-loaded partials - ✅ Proper Alpine availability checking - ✅ Small timeout for HTMX to ensure DOM is settled - ✅ Better error messages with prefixes for debugging

Expected Behavior After Fix

When clicking the Edit button on a saved query:

  1. Server side: Django view passes query_json containing saved filters
  2. Template execution: Django renders template variables
  3. Script execution: - QUERY_DATA object created and populated - JSON parsed and stored
  4. HTMX loads partial: htmx:afterSettle fires
  5. Alpine initialization: initializeEditQueryBuilder() registers component
  6. Alpine processing: x-data="queryBuilderEdit" component initializes with data
  7. Init callback: init() method calls loadQueryData()
  8. Groups loaded: Saved query groups and rules display in the UI

Browser Console Output

Expected logs (in order):

text
[SETUP] Filters by Model loaded: (2) ['user', 'entity']
[SETUP] Query JSON loaded: {condition: "AND", primary_model: "user", ...}
[Init] Setting up event listeners for Alpine initialization
[htmx:afterSettle] Event fired, initializing edit query builder
=== Initializing Edit Query Builder ===
Query ID: 1
Query Name: test query
Selected Model: user
Has Query JSON: true
[Alpine] Initializing tree for element: <div x-data="queryBuilderEdit">
[loadQueryData] Loaded filters for model: user
[loadQueryData] Loaded 1 groups from saved query: [{...}]

Files Modified

  1. eneza_workflow_engine/templates/saved_query/partials/edit_query_builder_partial.html - Added pre-parsing of template variables - Fixed Alpine data initialization - Improved loadQueryData() logic - Enhanced event listener setup

Testing Steps

  1. Navigate to a saved query detail page
  2. Click the "Edit" button
  3. Open DevTools Console (F12)
  4. Check that logs appear in order without errors
  5. Verify that saved query conditions appear in the query builder UI
  6. Test editing and updating the query

Debugging

If the fix doesn't work, check:

  1. Console logs - Verify data is being pre-parsed
  2. Network tab - Confirm HTMX request contains rendered HTML
  3. Alpine errors - Should show proper component initialization
  4. Query builder UI - Should display saved groups and rules visually