laravellaravel-livewirepusherlaravel-9laravel-echo

Laravel Echo "Echo cannot be found"


Getting a warning from Laravel Echo saying Laravel Echo cannot be found. All my scripts are configured correctly in terms of the order of loading livewire and app.js. I can console.log an instance of Echo in my bootstrap.js file and it's there, so I know it's being loaded.

However, anytime I reference my private channel in my livewire component like so:

public function getListeners()
{
    return [
        'refreshTransactions',
        'pushMissing',
        'setCurrentStep',
        "echo-private:statements.{$this->statement->id},StatementCompleted" => 'testingThis'
    ];
}

I get the warning. If I comment out that line I don't get the warning. I've also tried doing this in my js to just attempt to get Laravel Echo to recognize something in javascript, and it doesn't log anything:

Echo.channel('statements.6')
    .listen('StatementCompleted', (e) => {
        console.log(e);
});

I've verified my events are firing, and I can see them in the pusher debug console, so they're getting to pusher.

My event:

<?php

namespace App\Events;

use App\Models\Statement;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class StatementCompleted implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $statement;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Statement $statement)
    {
        $this->statement = $statement;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('statements.' . $this->statement->id);
    }

    public function broadcastWith()
    {
        return [
            'id' => $this->statement->id,
            'filename' => $this->statement->original_file_name,
            'parts_vendor_id' => $this->statement->parts_vendor_id,
            'uploaded_by' => $this->statement->uploaded_by
        ];
    }
}

This is my channel routes file, just returning true at the moment to try and get it to work:

Broadcast::channel('statements.{statementId}', function ($user, $statementId) {
    return true; 
    //(int) $user->id === (int) Statement::findOrNew($statementId)->uploaded_by;
});

In the above, I'm not sure if it's actually the statementId that's getting passed, or if it's the full statement object. This is my first attempt at broadcasting, so I have basically no idea what I'm doing.

Here is the docs from Livewire showing how to listen to a private channel event, and I'm following this exactly:

Livewire docs

UPDATE

My event gets fired, and if I include the following, Echo will log the event:

<script>
        Echo.private('statements.6')
            .listen('StatementCompleted', (e) => {
                console.log(e);
            });
    </script>

So I know Echo is working, but if I include the listener line in my Livewire component it breaks.

UPDATE

It's been a minute since I looked at this, but someone commented and wanted an update, so I'm adding what I believe was the ultimate solution. As I recall, I was having an issue with the order of including my livewire/alpine scripts and by breaking out my alpine import into a separate file, I was able to fix it.

I created an alpine-split.js file with this in it, and removed it from my bootstrap.js file:

import Alpine from 'alpinejs';

window.Alpine = Alpine;

Alpine.start();

And then in my main layout file I ordered the imports like this:

<script src="{{ mix('js/app.js') }}"></script>
    @livewireScripts
    <script src="{{ mix('js/alpine-split.js') }}"></script>

I no longer get the error after that.

UPDATE 2

Just saw the answer from @Alex, which is correct and basically what I described above.


Solution

  • Take a look at your master/app layout blade.

    Livewire installation instructions state that app.js must be called BEFORE @livewireScripts.

    This is true. Laravel Echo is called in bootstrap.js (which is included in app.js), so therefore it will be / must be loaded before livewire handle (@livewireScripts)

    Funny enough, if you are loading other libraries that depend on Livewire (like livewire-tables) or you are using alpine.js, you have to load these AFTER livewire so you can't load them in app/bootstrap.js...they have to be called in a separate js file AFTER the @livewireScripts handle.

    Hope this helps.