问题
I have a table products
and job_statuses
using this package: laravel-job-status
There is a column in job_statuses
called status
the package made this column finished
once the queue job is finished!
So I created a column in products
table called job_status_id
(relation with job_statuses) just to save the job status id in the products table to see if this job is finished!
Simply I created a component using Livewire
for just refresh single product when the job finished refresh the component:
class ProductIndex extends Component
{
public $product;
public function mount($product)
{
$this->product = $product;
}
public function render()
{
return view('livewire.merchant.product.index');
}
}
Inside product-index
component:
@if ($product->job_status->status == 'finished')
// show real image
@else
// show loader
@endif
My blade:
@foreach ($products as $product)
<livewire:merchant.product.product-index :product="$product" :key="$product->id">
@endforeach
How can refresh the product component if the status is finished?
回答1:
You can add an event-listener in your component, then fire an event from anywhere on the page - even from JavaScript - to refresh the component.
To add a listener to make the component refresh itself, simply add the following line to your ProductIndex
component.
protected $listeners = ['refreshProducts' => '$refresh'];
Livewire will now listen for any refreshProducts
events that are emitted, and once they are emitted, it will refresh that component.
You can also customize it further, by replacing the magic $refresh
action with a method, which you can pass parameters to. If you name the event the same as the method, you don't need to specify a key/value pair (eventname as the key, methodname as the value), and the value alone will suffice. Here's an example,
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
}
}
From within a Livewire component, you can emit events using
$this->emit('refreshProducts');`
// or if you are passing arguments, as with the second example,
$this->emit('refreshProducts', $productID);
You can also emit events from JavaScript, by doing
<script>
Livewire.emit('refreshProducts')
</script>
If you want to make the queue trigger that event once its complete, you need to implement something that either polls the server to ask "has the job finished" and then fire the event, or you can use Laravel Echo as a websocket. This will allow you to fire and listen for events from outside the Livewire ecosystem.
Polling
When you're polling, you don't have to emit an event for every update, as the component will refresh itself continuously .
Polling is the easiest way to continuously update a Livewire component, it does not require any websockets integration like Laravel Echo. This means that every X seconds (default is 2 seconds), your component is going to make an AJAX request to your server, to fetch its newest data, and re-render itself with the new data.
This is easily achieved by wrapping the component with the wire:poll
attribute - here's an example of using 5 seconds.
<div wire:poll.5s>
<!-- The rest of your view here -->
</div>
However, this means that all instances of that component will re-render themselves and fire an AJAX request of their own to the server to get the newest data. You might want to make a "parent" component for all your items, thereby just having 1 singular component re-render.
Broadcasting & Websockets
I'm going to assume that you have installed Laravel Echo already. Now - to achieve this functionality of broadcasting, you first need to create an event. Run the command
php artisan make:event ProductStatusUpdated
This will create an event in your app\Events
folder. Customize it to however you need it. It will look something like this after running the command,
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');
}
}
We can change the channel from PrivateChannel('channel-name')
to Channel('products')
, this allows us to listen for it in Livewire in the products channel (you could name the channel whatever you want, and you can also listen for private events - there's documentation for that in the Livewire documentation, referenced at the bottom of this answer).
So that means the broadcastOn
would look something like this
public function broadcastOn()
{
return new Channel('products');
}
Next, after the job has completed its work and all the statuses has been set, fire that event from Laravel, using
event(new \App\Events\ProductStatusUpdated);
Now we need to update our listener in the Livewire component, so that we can actually listen for the broadcast on that channel through Laravel Echo (and not the Livewire events that we did before).
protected $listeners = ['echo:products,ProductStatusUpdated' => 'refreshProducts'];
And we're done! You're now using Laravel Echo to broadcast an event, which Livewire intercepts, and then runs. The listener is now a key/value pair, where the value is still the method name in the component (you can still use the magic action $refresh
instead if you desire to), and the key is channel,event
prefixed by echo:
.
Resources:
- https://laravel-livewire.com/docs/2.x/events
- https://laravel-livewire.com/docs/2.x/laravel-echo
来源:https://stackoverflow.com/questions/64186425/how-to-refresh-the-component-after-the-queue-job-is-finished