Laravel Livewire导线:单击可创建无限循环

时间:2020-11-07 19:58:38

标签: laravel laravel-livewire

我有一个Livewire组件,它是一个产品过滤器。这些查询都可以正常工作,但有时会创建无限循环的请求。

您可以在下面的GIF中看到这种情况,它是Laravel Debugbar的捕获。我单击了一些过滤器,然后突然进入了该请求循环。

enter image description here

我专门在视图中的过滤器上使用wire:loading.attr="disabled",以便在请求仍在处理时,某人无法选择过滤器。

我的代码和一些背景:

Livewire组件

use App\Models\Product;
use App\Models\Brand;

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是因为,如果您取消选择颜色值,而不是从数组中删除颜色值,那么Livewire会将该键值设置为false。因此会将其放入查询中,将导致结果不准确。

Livewire视图和问题

这很好:

@foreach($all_brands as $brand)
    <input type="checkbox" name="brand.{{ $brand->id }}" 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" name="color.{{ $color->id }}" 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

这很奇怪,因为此刀片片段与$brands完全相同,如上所示:

唯一不同的是colors关系是hasMany的{​​{1}}与belongsTo的关系。

我现在在想这就是问题所在...

我尝试过的事情

  • brand添加到wire:key="brand.{{ $brand->id }}"元素
  • input元素周围的wire:key="brand.{{ $brand->id }}"添加到div
  • 按照评论中的建议使用inputwire:model="brand.{{ $brand->id }}"(以及我认为已解决的问题)
  • 使用wire:model="brand.{{ $loop->id }}",所以有一个唯一的键名
  • 删除wire:model="brand.b{{ $brand->id }}"方法(似乎不太可能是问题,只是为了测试)

控制台错误

最后,只有在发生无限循环时,我才会在控制台中出现这些错误,因此很可能是原因或结果。

array_filter

TypeError: null is not an object (evaluating 'directive.value.split')

两者都在Unhandled Promise Rejection: TypeError: null is not an object (evaluating 'directive.value.split')中,我认为这是Livewire Javascript文件。

这里似乎发生了错误:

LoadingStates.js

3 个答案:

答案 0 :(得分:3)

已在 GitHub 问题上回答,复制到此处供其他人查找。


问题是一个morphdom问题。

触发错误和循环的代码是 wire:loading 标题行和产品行。

原因是,当您选择两种或多种颜色时,没有显示结果。然后会发生什么是您从显示标题/产品/总计切换到显示空状态。

但 morphdom 不知道默认情况下它应该删除旧的 div 并添加新的 div。相反,它试图将旧的第一个 div“变形”为新的 div。这意味着 wire:loading 侦听器在不应注册时仍会注册。因此为什么会出现错误和循环。

虽然这是一个简单的修复。您需要在定义它们的 div 中添加连线键,以便 morphdom 知道它们实际上已经完全改变,并删除旧的并添加新的。

看看下面这张差异截图,了解我为让它工作所做的工作。我为这个文件中的所有顶级 div 添加了一个连线键。

建议每当使用这样的条件将线:键添加到条件内的第一级元素时,因此 morphdom 知道发生了变化。 VueJS 也有同样的问题,在循环中需要键。

Screenshot of diff

答案 1 :(得分:1)

因此,事实证明,如果您在这样的wire:model循环中使用foreach,则必须这样写:wire:model="brand.{{ $brand->id }}"。在文档中找不到它,因此希望可以对此处的其他人有所帮助。

更新

可以通过此方法解决无限循环,但是现在发生的事情是,将数组值设置为零,而不是一旦选中复选框,然后再次单击以取消选择,则将其从数组中删除。因此,whereIn将寻找值为0的品牌ID,该ID将不会返回结果。

更新2

循环实际上没有解决...请参阅原始问题。因为浪费了太多的时间和咖啡,所以在这笔$%$£#上悬赏。

答案 2 :(得分:1)

我看到几个问题:

您正在为<div>元素和<input>元素使用相同的wire:key。我会保持那些独特。

此外,您在输入元素上使用了相同的name。这将导致复选框出现问题,因为传递给后端的值具有相同的名称,我相信livewire会尝试基于该名称进行同步。将name中的input字段更改为唯一字段可能是答案。