-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Package
backpack/pro v3.0.3 (also affects earlier versions)
Backpack version
backpack/crud7.0.22backpack/pro3.0.3- PHP 8.3
- Laravel 12
Bug description
When a CRUD list view has more than one text filter, the page throws a JavaScript error:
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
at initTextFilter
This breaks the clear button functionality on all text filters except the last one defined.
Steps to reproduce
- Create a CrudController with two or more text filters:
$this->crud->addFilter([
'type' => 'text',
'name' => 'filter_one',
'label' => 'Filter One',
], false, function ($value) {
$this->crud->addClause('where', 'column_one', 'LIKE', "%$value%");
});
$this->crud->addFilter([
'type' => 'text',
'name' => 'filter_two',
'label' => 'Filter Two',
], false, function ($value) {
$this->crud->addClause('where', 'column_two', 'LIKE', "%$value%");
});- Load the list view in the browser.
- Open the browser console — the TypeError appears immediately.
Root cause
In vendor/backpack/pro/resources/views/filters/text.blade.php, the initTextFilter function is pushed via @push('after_scripts') once per text filter. Each push redefines the global initTextFilter function.
The problem is on line 33, where the clear button selector uses a hardcoded Blade variable:
let clearButton = filter.querySelector('.text-filter-{{ $filter->key }}-clear-button');Since @push appends content and the function is redefined each time, the last text filter's $filter->key wins. When initTextFilter is called for any earlier text filter, querySelector looks for the wrong class name, returns null, and clearButton.addEventListener(...) throws.
Expected behavior
The initTextFilter function should work correctly for all text filters, regardless of how many are on the page.
Suggested fix
Two changes in text.blade.php:
- Use the dynamic JS variable
filterKey(already extracted from the DOM on line 30) instead of the Blade variable:
- let clearButton = filter.querySelector('.text-filter-{{ $filter->key }}-clear-button');
+ let clearButton = filter.querySelector('.text-filter-' + filterKey + '-clear-button');- Change
@pushto@pushOnceso the function is only defined once (since it's now fully dynamic, there's no need to redefine it per filter):
- @push('after_scripts')
+ @pushOnce('after_scripts')
<script>
function initTextFilter(filter, filterNavbar) {
...
};
</script>
- @endpush
+ @endPushOnce