Recent works require me to work with Livewire and ChartJs.
Here how the expectation should be:
Change the filter, will update chart.
Sounds easy right? TLDR;
Part 1
Prepare our simple filter:
php artisan make:livewire Filter
The Filter
class:
<?php
namespace App\Http\Livewire\Dashboard;
use Livewire\Component;
class Filter extends Component
{
public $selectedOrganization = null;
public function onOrganizationChange()
{
$this->emit('organization-selected', $this->selectedOrganization);
}
public function render()
{
return view('livewire.filter');
}
}
The Livewire view file:
<div>
<div class="flex justify-end mt-4 px-4 w-full">
<div class="w-1/4">
<x-select wire:model.defer="selectedOrganization" wire:change="onOrganizationChange">
@foreach (organizations() as $organization)
<option value="{{ $organization->uuid }}">{{ $organization->name }}</option>
@endforeach
</x-select>
</div>
</div>
</div>
Assuming on organizations()
helper will do:
<?php
use App\Models\Organization;
if (! function_exists('organizations')) {
function organizations()
{
return Organization::orderBy('name')->get();
}
}
Now you have completed the Filter
Livewire component.
Use it in default resources/views/dashboard.blade.php
:
<x-app-layout>
<div class="min-h-full">
@livewire('filter')
</div>
</x-app-layout>
Part 2
Next, we a component to display the chart.
php artisan make:livewire Chart
Let's update our Livewire chart blade file:
@once
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@endpush
@endonce
@push('scripts')
<script>
const chart = new Chart(
document.getElementById('chart'), {
type: 'line',
data: {
labels: @json($labels),
datasets: @json($dataset)
},
options: {
plugins: {
legend: {
position: 'bottom'
}
},
responsive: true,
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
}
}
);
Livewire.on('updateChart', data => {
chart.data = data;
chart.update();
});
</script>
@endpush
<x-panel class="bg-white p-4 mx-4 mt-4">
<div>
<canvas id="chart"></canvas>
</div>
</x-panel>
Note that, there is few things happened here:
- The
@once
only use to include the ChartJs once in entire app. - The initialisation of the chart object I have
labels
anddatasets
is dynamically set via@json
blade directive. This just to simplify initial load and configuration of the chart object to be dynamic and configurable at the backend. -
Livewire.on
simply listen to theupdateChart
event. On listen, we will update the chart - this is where we want send our data.
Then the Chart
class:
<?php
namespace App\Http\Livewire\Dashboard;
use App\Models\Organization;
use Livewire\Component;
class Activity extends Component
{
public array $dataset = [];
public array $labels = [];
public Organization $organization;
protected $listeners = [
'organization-selected' => 'organizationSelected',
];
public function organizationSelected(string $uuid)
{
$this->organization = Organization::whereUuid($uuid)->first();
$labels = $this->getLabels();
$dataset = [
[
'label' => 'Logged In',
'backgroundColor' => 'rgba(15,64,97,255)',
'borderColor' => 'rgba(15,64,97,255)',
'data' => $this->getRandomData(),
],
];
$this->emit('updateChart', [
'datasets' => $dataset,
'labels' => $labels,
]);
}
public function mount()
{
$this->labels[] = $this->getLabels();
$this->dataset = [
[
'label' => 'Logged In',
'backgroundColor' => 'rgba(15,64,97,255)',
'borderColor' => 'rgba(15,64,97,255)',
'data' => $this->getRandomData(),
],
];
}
private function getLabels()
{
$labels = [];
for ($i = 0; $i < 12; $i++) {
$labels[] = now()->subMonths($i)->format('M');
}
return $labels;
}
private function getRandomData()
{
$data = [];
for ($i = 0; $i < count($this->getLabels()); $i++) {
$data[] = rand(10, 100);
}
return $data;
}
public function render()
{
return view('livewire.dashboard.activity');
}
}
The only we going to look at is how the data is populated. On organization selected, an event emitted from our Filter
and received in Chart
class in organizationSelected
method.
The Chart
will populate a new set of data.
In this case we create randomly the data for the pass 12 months.
Assuming we are looking at 12 months of logged in pattern of data.
Then the last part, the following piece of code will dispatch an event with the dataset and labes.
So that from Livewire.on()
will receive a new set of data and render a new chart.
$this->emit('updateChart', [
'datasets' => $dataset,
'labels' => $labels,
]);
Conclusion
The key point is the Event & Listener of the Livewire:
Configure the Livewire Listener on frontend side:
Livewire.on('updateChart', data => {
chart.data = data;
chart.update();
});
And emit event from Livewire backend side, to pass new data:
$this->emit('updateChart', $data);
With these two key points, hopes to help people work with dynamic charts with Livewire.
Top comments (1)
Thanks!