Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

valentin_vranic's avatar

Load script after render, re-render

I have a Livewire 3 component, which loads data from database and renders 6 charts based on data. I'm using google charts for the representation.

On first load everything works fine. I have a small form where I can set the from-to dates for getting data respectively. After filtering the data retrieved are correct, but the charts are not loaded. The application runs into render method too, but the code under @script('charts') @endscript directive is not loaded again, and the charts are not appearing.

https://livewire.laravel.com/docs/javascript#executing-scripts - here it is written that the code under @script directive is executed after page load but before component render. So why does it happen that it work on mount, first load, but when I modify, update property and re-render, then it's not. What would be a good approach for solving this?

Thanks in advance, Vale

1 like
7 replies
LaryAI's avatar
Level 58

Hello Vale,

The issue you're encountering is due to the way Livewire handles scripts when updating the DOM. The @script directive is indeed executed after the page load but before the component is rendered for the first time. However, when you update a Livewire component, it doesn't re-execute scripts within the @script directive because Livewire tries to be smart about DOM diffing and updating to avoid re-running scripts unnecessarily.

To solve this, you can use Livewire's JavaScript hooks to reinitialize your charts after Livewire has updated the DOM. Here's how you can do it:

  1. Remove the @script directive from your Livewire component's blade template.
  2. Add a JavaScript listener for Livewire's message.processed event, which is fired after Livewire has finished processing the update and the DOM has been updated.

Here's an example of how you might implement this:

<div wire:init="loadChartData">
    <!-- Your charts container here -->
    <div id="chart_div"></div>
</div>

<script>
    document.addEventListener('livewire:load', function () {
        // Function to initialize the charts
        function initCharts() {
            // Your Google Charts initialization code here
            google.charts.load('current', {'packages':['corechart']});
            google.charts.setOnLoadCallback(drawChart);

            function drawChart() {
                // Your chart drawing logic here
            }
        }

        // Initialize the charts on page load
        initCharts();

        // Reinitialize the charts after Livewire updates
        Livewire.hook('message.processed', (message, component) => {
            initCharts();
        });
    });
</script>

In this example, initCharts is a function that contains the logic for loading and drawing your Google Charts. This function is called once when the page loads (livewire:load event) and again every time Livewire finishes processing an update (message.processed hook).

Make sure to replace the chart initialization and drawing logic with your actual code. This approach ensures that your charts are reinitialized after every Livewire update, which should solve the issue of the charts not appearing after the data is filtered.

I hope this helps! Let me know if you have any further questions.

LaCoder's avatar

Hello Vale,

I am using the same way apexCharts, and I re-render chart data by below code, You need to send dispatch when you filter data and update chart data, Initially chart will load with blank data, and once you send filtered data, it will re-render the chart with updated properties,

The same way you can try with Google Charts,

Livewire.on('pnlChartDataUpdated', (chartData) => { This is main thing of listening event.

@push('scripts')
    <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
    <script>
        document.addEventListener('livewire:navigated', function() {
            var options = {
                series: [],
                chart: {
                    type: 'bar',
                    height: 350,

                },
                title: {
                    text: 'PnL Chart',
                    align: 'center',
                    style: {
                        fontSize: '16px',
                        fontWeight: 'bold'
                    }
                },
                plotOptions: {
                    bar: {
                        colors: {
                            ranges: [{
                                from: -10000000000,
                                to: 0,
                                color: '#fb4143'
                            }, {
                                from: 0,
                                to: 100000000000,
                                color: '#5cb85c'
                            }]
                        },
                        borderRadius: 0,

                    },

                    legend: {
                        position: 'right',
                        offsetY: 40
                    },
                },
                dataLabels: {
                    enabled: false,
                },

            };

            var chart = new ApexCharts(document.querySelector("#pnl_by_date_chart"), options);
            chart.render();

            Livewire.on('pnlChartDataUpdated', (chartData) => {


                var mtm = chartData.chartData.map(data => data.mtm);;
                var date = chartData.chartData.map(data => data.date);

                chart.updateOptions({
                    series: [{
                        name: 'PnL',
                        data: mtm
                    }],
                    yaxis: {
    
                        crosshairs: {
                            show: true,
                            position: 'back',
                            stroke: {
                                color: '#b6b6b6',
                                width: 1,
                                dashArray: 0,
                            },
                        },
                    },
                    xaxis: {
                        categories: date,
                        type: 'Date',
                    },

                })


            });
        }, { once: true });
    </script>
@endpush


1 like
LaCoder's avatar
LaCoder
Best Answer
Level 1

@valentin_vranic In my filter component i added this,


    #[On('journalDateFilterApplied')] 
    public function updateSelectedDate($startDate, $endDate)
    {
        $this->startDate = $startDate;
        $this->endDate = $endDate;
    }

    /**
     * Get the Profit and Loss (PnL) data based on the start and end dates, and dispatch an event with the updated data.
     */
    public function getPnLData()
    {
        $this->pnlChartData = DailyMtm::where('user_id', auth()->user()->id)->whereBetween('date', [$this->startDate, $this->endDate])->orderBy('date')->get();
        $this->dispatch('pnlChartDataUpdated', chartData:$this->pnlChartData);
    }

    public function render()
    {
        $this->getPnLData();
        return view('livewire.journal.dashboard-filter');
    }
1 like
valentin_vranic's avatar

@LaCoder I see. And however I don't really like the idea, that I have to pass the data like that, even if the LW public properties are gonna be visible in blade view (that's how I populate my charts). But if there is no other way around, that's how it is.

Thanks for the solution!

lucasweaver's avatar

Thanks for tyring, but I can't get that workaround to work for my situation @valentin_vranic .

I'm trying to load a set of $flashcards, and then after the user changes a filter to include learned cards, fetch the new group of $flashcards, and then show them one by one if they match the $loop->iteration.

    <div class="bg-gray-900 mb-4 min-h-screen min-w-screen pt-3 px-5 flex justify-center"> 
        @foreach($flashcards as $flashcard)        
        <div x-show="card == '{{ $loop->iteration }}' " wire:key="flashcard-{{ $flashcard->id }}">
            <div x-text="card" class="p-3 bg-red-300 text-white"></div>
             <div class="p-3 bg-blue-400 text-white">{{ $loop->iteration}}</div>            
            <div class="bg-gray-700 text-white">
            {{ $flashcard->term }}
            </div>
        </div>
        @endforeach
        <div>            
            <button class="bg-teal-400 text-white px-2 py-2 mt-4" wire:click='nextCard'>Next Card</button>
            <button class="bg-teal-400 text-white px-2 py-2 mt-4" wire:click='toggleLearned'>Toggle Learned</button>
        </div>
    </div>
</div>
<?php

namespace App\Livewire\Flashcards;

use Livewire\Component;
use App\Models\FlashcardSet;

class Study extends Component
{
    public $flashcardSetId;
    public $flashcards;
    public $includeLearned = true;
    public $card = 1;
    
    public function toggleLearned()
    {
        $this->includeLearned = !$this->includeLearned;
        $this->getFlashcards();
        $this->resetCard();
        $this->dispatch('flashcardsUpdated');     
    }

    public function resetCard()
    {
        $this->card = 1;        
    }

    public function nextCard()
    {
        $this->card++;
    }
    
    public function getFlashcards()
    {        
        if(!$this->includeLearned)
        {
            $flashcardSet = FlashcardSet::find($this->flashcardSetId);
            $user_id = auth()->id();
            $this->flashcards = $flashcardSet->flashcards()
                ->where(function ($query) use ($user_id) {
                    $query->doesntHave('userFlashcardProgress')
                        ->orWhereHas('userFlashcardProgress', function ($subQuery) use ($user_id) {
                            $subQuery->where('user_id', $user_id)
                                ->whereNotIn('status', ['learned', 'mastered']);
                        });
                })
                ->get();            
        } else {
            $flashcardSet = FlashcardSet::find($this->flashcardSetId);
            $this->flashcards = $flashcardSet->flashcards()->get();
        }
                
    }

    public function mount($flashcardSetId)
    {
        $this->flashcardSetId = $flashcardSetId;
        $this->getFlashcards();                
    }
    
    public function render()
    {        
        return view('livewire.flashcards.study');
    }
}

I suspected it was because Alpine can't get proper access to the $loop->iteration value after the re-render of the $flashcards. But I haven't been able to find a workaround at all.

Let's say there are 6 cards originally, after the reload, the 4th card (which is learned), won't show. But if I manually add + 1 to the count of "card" in the livewire dev tools, and then decrease one, it will show correctly.

But without that manual change, it won't update at all.

And sometimes after a few toggles, it will show the old 1 and 4 $loop->iteration with the same 1 for the card value.

valentin_vranic's avatar

@lucasweaver I'm not sure that I'm getting what is your issue, but let's make a try.

x-show="card == '{{ $loop->iteration }}' " because I don't see where are you setting the card in AlpineJs scope, so I'm guessing that it's supposed to be $card or $wire.card Livewire public variable, or?

Can you show me the code where you include the livewire component?

Please or to participate in this conversation.