ToxifiedHashkey
ToxifiedHashkey

Reputation: 267

Getting error "Typed property must not be accessed before initialization" - Laravel Livewire

Description

I am type hinting model properties and trying to delete the invitation data. Below is the error which its throwing me back. Please help me with it, as I am unable to spot what is it that I'm missing.

Typed property App\Http\Livewire\Backend\UserManagement\FormComponent\InvitationManagementModal::$invitation must not be accessed before initialization

Stripped-down, copy-pastable code snippets

<?php

namespace App\Http\Livewire\Backend\UserManagement\FormComponent;

use Livewire\Component;

use Livewire\WithPagination;
use App\Http\Livewire\Backend\DataTable\WithCachedRows;

use App\Models\Invitation;

class InvitationManagementModal extends Component
{
    use WithPagination, WithCachedRows;
    
    public $showInvitationManagementModal = false;
    public Invitation $invitation;

    protected $listeners = ['manageInvitation'];

    public function manageInvitation()
    {
        $this->showInvitationManagementModal = true;
    }

    public function deleteInvitation(Invitation $invitation)
    {
        $this->invitation->delete();
    }

    public function getInvitationRowsProperty()
    {
        return $this->cache(function () {
            $invitations = Invitation::where('registered_at', null)->paginate(5);
            return $invitations;
        });
    }

    public function render()
    {
        return view('livewire.backend.user-management.form-component.invitation-management-modal', ['invitations' => $this->invitationRows]);
    }
}
<div>
    <x-modal.stacked wire:model.defer="showInvitationManagementModal" id="scroll-lock">
        <x-slot name="title">Manage Invitation</x-slot>
        <x-slot name="description">Manage all the invitations which are yet to be accepted.</x-slot>
        <x-slot name="content">
            <div class="p-8 space-y-4">
                <ul class="flex flex-col divide divide-y w-full bg-white rounded-lg shadow">
                    @forelse($invitations as $key => $invitation)
                        <li class="flex flex-row">
                            <div class="flex flex-1 items-center px-8 py-4">
                                <div class="flex-1 mr-16">
                                    <div class="text-sm dark:text-white">
                                        {{ $invitation->email }}
                                    </div>
                                </div>
                                <button wire:click="deleteInvitation" class="text-right flex justify-end">
                                    <x-icon.trash />
                                </button>
                            </div>
                        </li>
                    @empty
                    @endforelse
                </ul>
                <div>
                    {{ $invitations->links() }}
                </div>
            </div>
        </x-slot>
        <x-slot name="footer">
            <x-button.secondary wire:click.defer="$set('showInvitationManagementModal', false)">Cancel</x-button.secondary>
        </x-slot>
    </x-modal.stacked>
</div>

Context

Upvotes: 4

Views: 11034

Answers (2)

Prospero
Prospero

Reputation: 2352

Try this:

public function deleteInvitation(Invitation $invitation)
{
    $this->invitation = $invitation;
    $this->invitation->delete();
} 

Upvotes: 0

Qirel
Qirel

Reputation: 26490

The other answers here both have some minor things to note about them. You don't have to check $invitation, because the typehinting Invitation makes Laravel use Model-Route-Binding, which fetches the corresponding record - or throws a HTTP 404 status code if not found.

Secondly, and this is the actual error you are currently seeing yourself, is that you don't have to do anything to the $this->invitation, since its not set. You should instead pass a parameter to the method.

When looping data in Livewire, it is always recommended to use wire:key, so that Livewire can keep track of each record in the loop.

So for the actual delete method, just call the delete method on the input-variable.

public function deleteInvitation(Invitation $invitation)
{
    $invitation->delete();
    // Emit an event to notify the user that the record was deleted
    // Refresh the parent component to remove the invitation from the list

}

For your blade, add wire:key to the first element in the loop and pass the ID to the method.
(so wire:click="deleteInvitation({{ $invitation->id }})" instead of wire:click="deleteInvitation").

@forelse($invitations as $key => $invitation)
    <li class="flex flex-row" wire:key="invitation_{{ $invitation->id }}">
        <div class="flex flex-1 items-center px-8 py-4">
            <div class="flex-1 mr-16">
                <div class="text-sm dark:text-white">
                    {{ $invitation->email }}
                </div>
            </div>
            <button wire:click="deleteInvitation({{ $invitation->id }})" class="text-right flex justify-end">
                <x-icon.trash />
            </button>
        </div>
    </li>
@empty
@endforelse

This in turn means that, since its never used, you can remove the declaration of the $invitation property of that class, the line just after public $showInvitationManagementModal = false;.

public Invitation $invitation;

Upvotes: 3

Related Questions