DEV Community

Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

29 1

Livewire & ChartJs

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
Enter fullscreen mode Exit fullscreen mode

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');
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Assuming on organizations() helper will do:

<?php

use App\Models\Organization;

if (! function_exists('organizations')) {
    function organizations()
    {
        return Organization::orderBy('name')->get();
    }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Part 2

Next, we a component to display the chart.

php artisan make:livewire Chart
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Note that, there is few things happened here:

  1. The @once only use to include the ChartJs once in entire app.
  2. The initialisation of the chart object I have labels and datasets 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.
  3. Livewire.on simply listen to the updateChart 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');
    }
}
Enter fullscreen mode Exit fullscreen mode

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,
]);
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

And emit event from Livewire backend side, to pass new data:

$this->emit('updateChart', $data);
Enter fullscreen mode Exit fullscreen mode

With these two key points, hopes to help people work with dynamic charts with Livewire.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (2)

Collapse
 
gabiroman profile image
Gabriel Roman •

Thanks!

Collapse
 
faid_fadjri_ce1d7a01d38e5 profile image
Faid Fadjri •

i just follow your tutorial but when i change the filter the chart not changing

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more