如何在队列作业完成后刷新组件?

13

我有一个名为 productsjob_statuses 的表,使用了这个包:laravel-job-status

job_statuses 表中有一个名为 status 的列,该包在队列作业完成后将该列更改为 finished

因此,我在 products 表中创建了一个名为 job_status_id 的列(与 job_statuses 相关联),只是为了在产品表中保存作业状态 ID,以查看是否已完成此作业!

简单地说,我使用 Livewire 创建了一个组件,仅在作业完成时刷新单个产品并刷新组件:

class ProductIndex extends Component
{
    public $product;


    public function mount($product)
    {
        $this->product = $product;
    }

    public function render()
    {
        return view('livewire.merchant.product.index');
    }
}

product-index组件内:

@if ($product->job_status->status == 'finished')
  // show real image
 
@else
  // show loader 
@endif

我的剑:

@foreach ($products as $product)
  <livewire:merchant.product.product-index :product="$product" :key="$product->id">
@endforeach

如果产品状态为已完成,如何刷新产品组件?

2个回答

43
您可以在组件中添加事件监听器,然后从页面上的任何位置 - 甚至从 JavaScript 中 - 触发事件以刷新组件。
要添加监听器使组件自刷新,请仅将以下行添加到您的 ProductIndex 组件中。
protected $listeners = ['refreshProducts' => '$refresh'];

Livewire现在将会监听所有被触发的refreshProducts事件,一旦它们被触发,它就会刷新该组件。

你还可以进一步自定义它,通过用一个方法替换魔法的$refresh操作,并且你可以向该方法传递参数。如果你将事件命名为相同的方法名,则无需指定键/值对(将事件名作为键,方法名作为值),值本身就足够了。以下是一个例子:

protected $listeners = ['refreshProducts'];

// ...

public function refreshProducts($product_id = null) 
{
    // Refresh if the argument is NULL or is the product ID
    if ($product_id === null || $product_id === $this->product->id) {
        // Do something here that will refresh the event
    }
}

在 Livewire 组件内部,您可以使用以下方式触发事件:

$this->emit('refreshProducts');` 

// or if you are passing arguments, as with the second example, 
$this->emit('refreshProducts', $productID);

你还可以通过执行以下操作从JavaScript中发出事件:

<script>
    Livewire.emit('refreshProducts')
</script>

如果您想使队列完成后触发该事件,您需要实现一些东西,即轮询服务器以询问“作业是否已完成”,然后触发事件,或者您可以使用Laravel Echo作为websocket。这将允许您从Livewire生态系统外部触发和侦听事件。

Polling

在进行轮询时,您不必为每个更新发出事件,因为组件将连续刷新自身。

轮询是持续更新Livewire组件的最简单方法,与Laravel Echo等Websocket集成不同,它不需要任何Websocket集成。这意味着每隔X秒(默认为2秒),您的组件将向服务器发送AJAX请求,以获取其最新数据,并使用新数据重新渲染自身。

这很容易通过将组件包装在wire:poll属性中来实现-以下是使用5秒的示例。

<div wire:poll.5s>
    <!-- The rest of your view here -->
</div>

然而,这意味着该组件的所有实例都会重新渲染并向服务器发出自己的AJAX请求以获取最新数据。您可能希望为所有项目创建一个“父”组件,从而只有一个组件重新渲染。

广播和Websockets

我假设您已经安装了Laravel Echo,现在 - 要实现广播功能,你需要首先创建一个事件。运行以下命令:

php artisan make:event ProductStatusUpdated

这将在您的app\Events文件夹中创建一个事件。按照您的需求自定义它。运行该命令后,它将看起来像这样:

class ProductStatusUpdated
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}
我们可以将频道从PrivateChannel('channel-name')更改为Channel('products'),这使我们能够在Livewire中监听products频道中的内容(您可以随意命名频道,并且还可以监听私有事件 - Livewire文档中有相关文档,在本答案底部引用)。所以这意味着broadcastOn看起来会像这样。
public function broadcastOn()
{
    return new Channel('products');
}

接下来,在工作完成且所有状态已设置后,使用 Laravel 触发该事件:

event(new \App\Events\ProductStatusUpdated);

现在我们需要更新Livewire组件中的监听器,以便我们可以通过Laravel Echo在该频道上实际监听广播(而不是之前使用的Livewire事件)。

protected $listeners = ['echo:products,ProductStatusUpdated' => 'refreshProducts'];

完成了!您现在正在使用 Laravel Echo 广播事件,Livewire 拦截并运行它。监听器现在是一个键值对,其中值仍然是组件中的方法名(如果需要仍然可以使用魔术操作$refresh),而键是以 echo: 前缀的 channel,event

资源:


如果你想使用WebSockets,那么它们可以与Livewire完美地集成。或者,您需要使用轮询(这会使Livewire每X秒重新渲染组件),我可以更新答案以包括该示例。 - Qirel
不,我不想使用Websockets,我的问题是如果我必须安装Websockets,那么我将遵循第一种方式;如果不需要安装,我将使用你提到的第二种方式! - user14375528
但是在队列完成后,轮询将继续刷新! - user14375528
是的,轮询意味着页面将始终在打开的情况下,在X秒后(默认为2秒,但我已经展示了如何将其设置为5秒)刷新自己。 - Qirel
1
Laravel Echo是一个websocket,pusher是一个单独的包,但与Echo结合使用非常方便。请按照手册https://laravel.com/docs/8.x/broadcasting#installing-laravel-echo进行安装。 - Qirel
显示剩余3条评论

1

更简单的方法是直接调用挂载函数,如下所示:

public function mount()
{
    //some code to run on mount
}

public function refreshComponent()
{
    $this->mount();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接