From 8d03a46cd076e0fc44420500acf7ca58750fef2f Mon Sep 17 00:00:00 2001
From: Mohamed Eltayar <152964073+maltayyar2@users.noreply.github.com>
Date: Fri, 29 Aug 2025 21:23:37 +0300
Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20FIX=20INFINITE=20LOOP:=20Prevent?=
=?UTF-8?q?=20search=20reload=20cycles?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
CRITICAL FIXES:
- Added _searchInProgress flag to prevent infinite search loops
- Removed trigger_up('reload') that was causing view re-renders during search
- Enhanced _renderView to only re-apply search on manual page loads, not during search operations
- Replaced _forceUpdateMainPager with safe _updatePagerOnly that uses direct DOM updates
- Added comprehensive loop prevention in _performSearch and _clearSearch
- Fixed infinite cycle between search → pager update → view reload → search
This resolves the flickering issue where search results would appear correctly for a split second then revert to showing all records in an endless loop.
---
.../static/src/js/list_search.js | 149 +++++++++++-------
1 file changed, 93 insertions(+), 56 deletions(-)
diff --git a/odex25_base/fims_general_search_tree_view/static/src/js/list_search.js b/odex25_base/fims_general_search_tree_view/static/src/js/list_search.js
index 14c8ba4f2..3c03eab67 100644
--- a/odex25_base/fims_general_search_tree_view/static/src/js/list_search.js
+++ b/odex25_base/fims_general_search_tree_view/static/src/js/list_search.js
@@ -30,9 +30,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_renderView: function () {
var self = this;
- // Store search state before render
+ // PREVENT INFINITE LOOP: Don't re-trigger search during render
var searchValue = this._search ? this._search.value : '';
- var hasActiveSearch = searchValue && searchValue.length > 0;
+ var isCurrentlySearching = this._search && this._search.isFiltered;
return this._super.apply(this, arguments).then(function () {
// Add search box if needed
@@ -47,10 +47,11 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
// Always restore search UI
self._restoreSearchUI();
- // If we had an active search, re-apply it after a short delay
- if (hasActiveSearch) {
+ // CRITICAL FIX: Only re-apply search if it's a manual page load, NOT during search operations
+ if (searchValue && !isCurrentlySearching && !self._searchInProgress) {
+ console.log('Re-applying search after page load:', searchValue);
setTimeout(function() {
- if (self._search && self._search.value) {
+ if (self._search && self._search.value && !self._searchInProgress) {
self._performSearch(self._search.value);
}
}, 100);
@@ -71,7 +72,7 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
'' +
'' +
'' +
- '' +
+ '' +
'';
this.$el.prepend($(html));
},
@@ -92,6 +93,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
isFiltered: false,
lastSearchPromise: null
};
+ // Add flag to prevent infinite loops
+ this._searchInProgress = false;
}
},
@@ -161,24 +164,38 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
},
/**
- * Perform search - THE MAIN SEARCH LOGIC
+ * Perform search - ENHANCED WITH LOOP PREVENTION
*/
_performSearch: function(value) {
var self = this;
+ // PREVENT INFINITE LOOPS
+ if (this._searchInProgress) {
+ console.log('Search already in progress, ignoring');
+ return Promise.resolve();
+ }
+
// Use mutex to prevent concurrent searches
return this._searchMutex.exec(function() {
+ // Set flag to prevent loops
+ self._searchInProgress = true;
+
// Clear if empty
if (!value) {
- return self._clearSearch();
+ return self._clearSearch().finally(function() {
+ self._searchInProgress = false;
+ });
}
// Check prerequisites
if (!self.state || !self.state.model) {
console.error('Missing model information');
- return;
+ self._searchInProgress = false;
+ return Promise.resolve();
}
+ console.log('=== Starting search for:', value, '===');
+
// Store original data on first search
if (!self._search.originalData && !self._search.isFiltered) {
self._storeOriginalData();
@@ -227,6 +244,9 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}
}).finally(function() {
self._showLoading(false);
+ // CRITICAL: Clear the progress flag
+ self._searchInProgress = false;
+ console.log('=== Search completed ===');
});
});
},
@@ -237,6 +257,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
_processSearchResults: function(records) {
var count = records.length;
+ console.log('Processing search results:', count, 'records');
+
// Update search state
this._search.isFiltered = true;
this._search.allFilteredRecords = records;
@@ -247,8 +269,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
if (count === 0) {
this._showNoResults();
- // CRITICAL: Update main pager for empty results
- this._forceUpdateMainPager(0);
+ // Update main pager for empty results - WITHOUT RELOAD
+ this._updatePagerOnly(0);
return;
}
@@ -257,22 +279,20 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
},
/**
- * Update view with filtered data - FIXED CRITICAL LOGIC
+ * Update view with filtered data - ENHANCED TO PREVENT LOOPS
*/
_updateViewWithFilteredData: function(records) {
console.log('=== _updateViewWithFilteredData ===');
console.log('Records count:', records.length);
console.log('Filtered IDs:', this._search.filteredIds);
- // ALWAYS try to update the view, regardless of DOM filtering success
-
// Method 1: Try DOM filtering first (fastest if it works)
var domFilterWorked = this._filterExistingRows();
console.log('DOM filtering worked:', domFilterWorked);
if (domFilterWorked) {
- // DOM filtering worked, but STILL update pager properly
- this._forceUpdateMainPager(records.length);
+ // DOM filtering worked, update pager WITHOUT causing reload
+ this._updatePagerOnly(records.length);
return;
}
@@ -300,7 +320,6 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
console.log('=== DOM Filtering Debug ===');
console.log('Total rows in DOM:', $allRows.length);
console.log('Looking for IDs:', this._search.filteredIds);
- console.log('Limit:', limit);
// First, collect all available row IDs for debugging
$allRows.each(function(index) {
@@ -339,13 +358,11 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
visibleCount++;
foundAnyMatch = true;
console.log('✓ Showing row', index, 'with ID', rowId);
- } else {
- console.log('✗ Hiding row', index, 'with ID', rowId, '(over limit)');
}
}
});
- console.log('DOM filtering completed - Visible rows:', visibleCount, 'Found matches:', foundAnyMatch);
+ console.log('DOM filtering completed - Visible rows:', visibleCount);
return foundAnyMatch && visibleCount > 0;
},
@@ -381,21 +398,21 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._showNoResults();
}
- // CRITICAL: Force update main pager
- this._forceUpdateMainPager(records.length);
+ // Update pager WITHOUT causing reload
+ this._updatePagerOnly(records.length);
},
/**
- * CRITICAL FIX: Force update main Odoo pager
+ * SAFE PAGER UPDATE - WITHOUT RELOAD TRIGGERS
*/
- _forceUpdateMainPager: function(totalCount) {
- console.log('=== Updating Main Pager ===');
+ _updatePagerOnly: function(totalCount) {
+ console.log('=== Safe Pager Update ===');
console.log('Total count:', totalCount);
- // Method 1: Try standard pager update
- if (this.getParent() && this.getParent().pager) {
+ // Method 1: Try standard pager update (SAFEST - no reload)
+ if (this.getParent() && this.getParent().pager && this.getParent().pager.updateState) {
try {
- console.log('Trying method 1: getParent().pager.updateState');
+ console.log('Trying method 1: safe pager updateState');
this.getParent().pager.updateState({
current_min: totalCount > 0 ? 1 : 0,
size: totalCount,
@@ -408,13 +425,13 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
}
}
- // Method 2: Try alternative pager access
+ // Method 2: Try alternative pager access (SAFE)
try {
var controller = this.getParent();
while (controller && !controller.pager) {
controller = controller.getParent();
}
- if (controller && controller.pager) {
+ if (controller && controller.pager && controller.pager.updateState) {
console.log('Trying method 2: controller.pager');
controller.pager.updateState({
current_min: totalCount > 0 ? 1 : 0,
@@ -428,30 +445,14 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
console.warn('Method 2 failed:', err);
}
- // Method 3: Direct state update + trigger change
+ // Method 3: Direct DOM update (SAFEST FALLBACK - no triggers)
try {
- console.log('Trying method 3: direct state update');
- if (this.state) {
- this.state.count = totalCount;
- // Trigger a state change event
- this.trigger_up('reload', {
- keepChanges: true,
- currentId: this.state.res_id
- });
- console.log('✓ Method 3 triggered');
- }
- } catch (err) {
- console.warn('Method 3 failed:', err);
- }
-
- // Method 4: Update DOM directly if all else fails
- try {
- console.log('Trying method 4: direct DOM update');
+ console.log('Trying method 3: direct DOM update');
var $pager = this.$el.closest('.o_content').find('.o_pager_value');
if ($pager.length) {
var displayText = totalCount > 0 ? '1-' + Math.min(totalCount, this.state.limit || 80) : '0';
$pager.text(displayText);
- console.log('✓ Method 4 succeeded');
+ console.log('✓ Method 3 succeeded - updated pager value');
}
var $pagerSize = this.$el.closest('.o_content').find('.o_pager_size');
@@ -459,9 +460,26 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
$pagerSize.text(totalCount);
console.log('✓ Updated pager size directly');
}
+
+ // Also try standard pager selectors
+ var $standardPager = $('.o_pager_value');
+ if ($standardPager.length) {
+ var displayText = totalCount > 0 ? '1-' + Math.min(totalCount, this.state.limit || 80) : '0';
+ $standardPager.text(displayText);
+ console.log('✓ Updated standard pager');
+ }
+
+ var $standardSize = $('.o_pager_size');
+ if ($standardSize.length) {
+ $standardSize.text(totalCount);
+ console.log('✓ Updated standard pager size');
+ }
} catch (err) {
- console.warn('Method 4 failed:', err);
+ console.warn('Method 3 failed:', err);
}
+
+ // REMOVED: Method that caused infinite loop (trigger_up reload)
+ console.log('Pager update completed');
},
/**
@@ -658,18 +676,27 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._showNoResults();
}
- // CRITICAL: Update main pager
- this._forceUpdateMainPager(visible);
+ // Update pager safely
+ this._updatePagerOnly(visible);
console.log('Client-side search completed. Visible:', visible, 'IDs:', matchedIds);
},
/**
- * Clear search - ENHANCED
+ * Clear search - ENHANCED WITH LOOP PREVENTION
*/
_clearSearch: function() {
console.log('=== Clearing search ===');
+ // PREVENT LOOPS during clear
+ if (this._searchInProgress) {
+ console.log('Search in progress during clear, setting flag to clear after');
+ this._pendingClear = true;
+ return Promise.resolve();
+ }
+
+ this._searchInProgress = true;
+
// Clear UI
this.$('.oe_search_input').val('');
this.$('.oe_clear_search').hide();
@@ -683,6 +710,8 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
this._search.isFiltered = false;
this._search.lastSearchPromise = null;
+ var promise;
+
// Restore original data
if (this._search.originalData) {
console.log('Restoring original data:', this._search.originalData);
@@ -705,12 +734,14 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
$(this).toggle(index < limit);
});
- // CRITICAL: Restore main pager
- this._forceUpdateMainPager(this._search.originalData.count);
+ // Restore pager safely
+ this._updatePagerOnly(this._search.originalData.count);
// Clear stored original data
this._search.originalData = null;
this._search.originalDomain = null;
+
+ promise = Promise.resolve();
} else {
// Just show all rows with limit
var $rows = this.$('.o_data_row');
@@ -724,11 +755,17 @@ odoo.define('fims_general_search_tree_view.list_search', function (require) {
});
if (this.state) {
- this._forceUpdateMainPager(this.state.count || $rows.length);
+ this._updatePagerOnly(this.state.count || $rows.length);
}
+
+ promise = Promise.resolve();
}
+ // Clear flags
+ this._searchInProgress = false;
console.log('Search cleared successfully');
+
+ return promise;
},
/**