Search code examples
laravellaravel-livewire

load data after the page load in livewire


Inside a view I have a livewire component that receives a list of coins from the api And are displayed inside the table.Now I want the page to load first and then load my component and display the list of coins.How can I do this? My code is such that data is received before the page loads

namespace App\Http\Livewire\Backend\Crypto;

use Livewire\Component;

class Cryptolist extends Component
{
    public function render()
    {
        try {
            $api = new \Binance\API('api','secret');
            $prices = $api->coins();
            $one = json_encode($prices, true);

            $coins = json_decode($one , true);
            return view('livewire.backend.crypto.cryptolist')->with('coins' , $coins);
        }catch(\Exception $e)
        {
            return view('wrong')->with('e' , $e);
        }
    }
}

this is view

@extends('backend.layouts.app')

@section('title', __('User Management'))

@section('breadcrumb-links')
    @include('backend.auth.user.includes.breadcrumb-links')
@endsection

@section('content')
    <div class="components">
        <div class="card card-bordered">
            <div class="card-inner">
                <livewire:backend.crypto.cryptolist />
            </div>
        </div>
        <!-- .card-preview -->
    </div>
    </div>
@endsection

and this is component for show data :

<div wire:ignore wire:init="init">
    <div id="loadesh1">
    <table class="datatable-init nk-tb-list nk-tb-ulist" data-auto-responsive="false">
        <thead>
        <tr class="nk-tb-item nk-tb-head">
            <th class="nk-tb-col"><span class="sub-text">name</span></th>
            <th class="nk-tb-col tb-col-mb"><span class="sub-text">balance</span></th>
        </tr>
        </thead>
        <tbody>
        @foreach ($coins as $item => $value)
            <tr class="nk-tb-item">
                <td class="nk-tb-col">
                    <div class="user-card">
                        <div class="user-avatar d-none d-sm-flex">

                            @if(file_exists(public_path() . '/img/crypto/'.strtolower($value['coin'].".svg")))
                                <img style="border-radius: 0" src="{{asset('/img/crypto/'.strtolower($value['coin']))}}.svg" class="img-fluid" alt="">
                            @else
                                <img style="border-radius: 0" src="https://demo.rayanscript.ir/-/vendor/cryptocurrency-icons/32/color/noimage.png" class="img-fluid" alt="">
                            @endif

                        </div>
                        <div class="user-info">
                            <span class="tb-lead english" style="font-weight: bolder">{{$value['name']}}</span>
                            <span class="english">{{$value['coin']}}</span>
                        </div>
                    </div>
                </td>
                <td class="nk-tb-col tb-col-mb" data-order="{{$value['coin']}}">
                    <div class="btn-group" aria-label="Basic example">
                        <button type="button" class="btn btn-sm btn-dim btn-light" wire:click="getBalance('{{$value['coin']}}')">
                            <div wire:loading wire:target="getBalance('{{$value['coin']}}')">
                                <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                            </div>
                            <span class="w-120px" id="coin-{{$value['coin']}}">get balance</span>
                        </button>
                        <button type="button" class="btn btn-sm btn-dim btn-primary">add coin</button>
                    </div>
                </td>
            </tr><!-- .nk-tb-item  -->
        @endforeach
        </tbody>
    </table>
    </div>
    </div>
</div>

Solution

  • There's a directive in Livewire called wire:init. This accepts a method in your component that gets run after the first, initial render.

    Declare a property in your class that determine if you should load data or not. Set it to false by default, and set it to true in your method that gets called on initialization.

    Be cautious to set your other properties to a state where your view still renders without the data, as you see the code below sets an empty array before the initial load.

    class Cryptolist extends Component
    {
        public bool $loadData = false;
    
        public function init()
        {
            $this->loadData = true;
        }
    
        public function render()
        {
            try {
                if ($this->loadData) {
                    $api = new \Binance\API('api','secret');
                    $prices = $api->coins();
                    $one = json_encode($prices, true);
    
                    $coins = json_decode($one , true);
                } else {
                    $coins = [];
                }
                return view('livewire.backend.crypto.cryptolist')->with('coins' , $coins);
            }catch(\Exception $e)
            {
                return view('wrong')->with('e' , $e);
            }
        }
    }
    

    Then in the root element in your blade view, put

    wire:init="init"
    

    https://laravel-livewire.com/docs/2.x/defer-loading


    Seeing your view after your edit, and that you are having trouble with your wire:ignore - here's how to work around that: wrap your table and code that is supposed to be ignored, inside a conditional of @if ($loadData). That way the Livewire component can re-render and push the table with the data after its loaded, and show a loading-text before the data is loaded.

    <div wire:init="init">
        @if ($loadData)
            <div id="loadesh1" wire:ignore>
                <table class="datatable-init nk-tb-list nk-tb-ulist" data-auto-responsive="false">
                    <!-- Rest of your table -->
                </table>
            </div>
        @else
            Loading data...
        @endif
    </div>