🔥 FIX INFINITE LOOP: Prevent search reload cycles

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.
This commit is contained in:
Mohamed Eltayar 2025-08-29 21:23:37 +03:00
parent 9bd93f5138
commit 8d03a46cd0
1 changed files with 93 additions and 56 deletions

View File

@ -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) {
'<input type="text" class="oe_search_input" placeholder="' + _t('Search...') + '" style="flex: 1; border: 1px solid #ccc; height: 32px; padding: 0 12px; border-radius: 4px;">' +
'<button class="btn btn-sm btn-secondary oe_clear_search ml-2" style="display: none;">' + _t('Clear') + '</button>' +
'<span class="oe_search_count ml-2" style="display: none; color: #6c757d; font-size: 0.9em;"></span>' +
'<span class="oe_search_loading ml-2" style="display: none;"><i class="fa fa-spinner fa-spin"></i></span>' +
'<span class="oe_search_loading ml-2" style="display: none;"><i class="fa fa-spinner fa-spin\"></i></span>' +
'</div>';
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;
},
/**