【发布时间】:2021-02-20 04:52:31
【问题描述】:
我有一个 Livewire 组件,它是一个产品过滤器。查询都可以正常工作,但有时会创建无限循环的请求。
您可以在下面的 GIF 中看到发生的事情,该 GIF 是 Laravel 调试栏的捕获。我点击了一些过滤器,然后突然进入了这个请求循环。
我在视图中的过滤器上专门使用了wire:loading.attr="disabled",因此在请求仍在处理时,有人无法选择过滤器。
我的代码和一些背景:
Livewire 组件
use App\Models\Product;
use App\Models\Brand;
use App\Models\Color;
class SearchProducts extends Component
{
public ?array $brand = [];
public ?array $color = [];
protected $queryString = ['brand', 'color'];
public function render()
{
$products = Product::query();
$products = $products->with('brand');
$products = $products->with('colors');
$products = $this->filterBrands($products);
$products = $this->filterColors($products);
$products = $products->paginate(24);
return view('livewire.search-products', [
'all_brands' => Brand::where('status', 'active')->get(),
'all_colors' => Color::where('status', 'active')->get(),
])->extends('app');
}
public function filterBrands($query)
{
$queryFilterBrand = array_filter($this->brand);
return empty($queryFilterBrand) ? $query : $query->whereIn('brand_id', $queryFilterBrand);
}
public function filterColors($query)
{
$queryFilterColor = array_filter($this->color);
return empty($queryFilterColor) ? $query : $query->whereHas('colors', function ($q) use ($queryFilterColor) {
$q->whereIn('color_id', $queryFilterColor);
});
}
}
我使用array_filter 的原因是,如果我取消选择颜色值并使用键中的字符 (wire:model="brand.b{{ $brand->id }}"),而不是从数组中删除它,Livewire 会将键值设置为 false。那么这个false 值将被放入查询中,这将给出不准确的结果。
Livewire 观点和问题
这很好用:
@foreach($all_brands as $brand)
<input type="checkbox" value="{{ $brand->id }}" id="brand.{{ $brand->id }}" wire:model="brand.{{ $brand->id }}" wire:loading.attr="disabled">
<label class="search-label search-wide-label mb-2" for="brand.{{ $brand->id }}">{{ $brand->title }} <i class="fal fa-times float-right selected-icon"></i></label>
@endforeach
但是,当我依次选择 2 种或更多颜色时,或者如果我选择 1 种颜色然后取消选择它,这会创建一个无限循环。所以似乎问题发生在第二次交互之后:
@foreach($all_colors as $color)
<input type="checkbox" value="{{ $color->id }}" id="color.{{ $color->id }}" wire:model="color.{{ $color->id }}" wire:loading.attr="disabled">
<label class="search-label search-wide-label mb-2" for="color.{{ $color->id }}">{{ $color->title }} <i class="fal fa-times float-right selected-icon"></i></label>
@endforeach
这很奇怪,因为这个刀片 sn-p 与 $brands 完全相同,如上所示:
唯一不同的是colors 关系是hasMany 与belongsTo 对应brand。
我现在认为这就是问题所在......
我尝试过但没用的东西
- 删除
$all_colors的@foreach循环并将过滤器放入纯HTML 中(以检查问题是否与循环有关) - 将
wire:key="brand.{{ $brand->id }}"添加到input元素 - 在
input元素周围的div中添加wire:key="brand.{{ $brand->id }}" - 按照 cmets 中的建议使用
wire:model="brand.{{ $brand->id }}"或wire:model="brand.{{ $loop->id }}"(我认为可以解决问题) - 使用
wire:model="brand.b{{ $brand->id }}",所以有一个唯一的键名 - 删除
array_filter方法(这似乎不太可能是问题,只是为了测试) - 使用按钮而不是复选框
- 使用
defer、lazy和/或debounce - 请专家尝试修复它...
控制台错误
最后一点,我的控制台只有在发生无限循环时才会出现此错误,因此这很可能是原因或结果。
TypeError: null is not an object (evaluating 'directive.value.split')
Unhandled Promise Rejection: TypeError: null is not an object (evaluating 'directive.value.split')
两者都在LoadingStates.js 中,我认为这是一个 Livewire Javascript 文件。
这里似乎发生了错误:
function startLoading(els) {
els.forEach(({ el, directive }) => {
if (directive.modifiers.includes('class')) {
let classes = directive.value.split(' ').filter(Boolean)
【问题讨论】:
-
您在循环中使用 wire:model="brand" ,并且每当复选框输入值更改时,这将触发新的 ajax 请求以重新渲染视图,然后 foreach 循环将更改复选框输入值触发另一个 ajax 等等。
-
谢谢。我不认为我理解“foreach 循环将更改复选框输入值”。由于 Livewire 的工作方式,带有
wire:model="brand"的复选框将使该数组在整个请求中保持同步? -
我的意思是复选框的“value”属性,value="{{ $brand->id }}" 这将在循环时发生变化,并且模型正在监听变化。我看到了你的回答,是的,我认为这会解决你的问题。