Przekazywanie parametrów, Sloty i re-używalne komponenty w Livewire

Tworzenie komponentów wielokrotnego użytku to podstawa skalowalnych aplikacji. W kontekście Livewire (w połączeniu z ekosystemem Laravel) mamy kilka sposobów na przekazywanie parametrów do komponentów, wykorzystywanie slotów oraz budowanie bardziej złożonych struktur takich jak modale.

W niniejszym artykule omówimy:

  1. Jak przekazywać parametry do komponentu Livewire już w momencie jego wywołania.
  2. Czym jest i jak działa metoda mount($param).
  3. Jak wykorzystać mechanizmy slotów Blade – również wewnątrz komponentów Livewire.
  4. Jak stworzyć re-używalny komponent, np. Modal, który może wyświetlać treść z innego komponentu i reagować na zdarzenia (otwarcie, zamknięcie, potwierdzenie akcji).

Artykuł nawiązuje do poprzednich wpisów:

Przekazywanie parametrów do komponentu Livewire

1. Metoda mount($param)

W Livewire możemy definiować metodę mount(), która zostanie wywołana podczas inicjalizacji komponentu. Pozwala ona wstępnie zainicjalizować dane w komponencie:

				
					use Livewire\Component;

class ExampleComponent extends Component
{
    public $someData;

    public function mount($dataFromParent)
    {
        $this->someData = $dataFromParent;
    }

    public function render()
    {
        return view('livewire.example-component');
    }
}

				
			

Dzięki temu, gdy odpalimy komponent za pomocą:

				
					<livewire:example-component :data-from-parent="$value" />

				
			

Parametr $dataFromParent automatycznie trafi do metody mount(). Jest to wyjątkowo przydatne w sytuacji, gdy chcemy wstępnie ustawić stan komponentu na podstawie danych z rodzica lub kontekstu (np. aktualnie zalogowanego użytkownika).

2. Przekazywanie danych przez atrybuty komponentu

W widoku Blade możemy przekazywać dane do komponentu w następujący sposób:

				
					
<livewire:example-component
    :some-prop="$user"
    :another-prop="['key' => 'value']"
/>

				
			

W klasie Livewire możemy wtedy zdefiniować publiczne właściwości o tych samych nazwach:

				
					public $someProp;
public $anotherProp;

public function mount()
{
    // Te właściwości już zawierają przekazane wartości
}

				
			

Można też użyć metody mount($someProp, $anotherProp) i zmapować przekazane właściwości do parametrów funkcji. Oba podejścia są poprawne – wybór zależy od preferencji i potrzeb.

Tworzenie komponentów z slotami

1. Sloty w Blade

Laravel Blade posiada mechanizm slotów, dzięki któremu możemy w strukturze naszego komponentu wyznaczyć obszary, do których „dostarcza się” zawartość z zewnątrz.
Najprostszy przykład (tradycyjny Blade, bez Livewire):

				
					
<div class="wrapper">
    <header>Header</header>
    <div class="content">
        {{ $slot }}
    </div>
    <footer>Footer</footer>
</div>

				
			

Aby użyć takiego layoutu:

				
					<x-layout>
    <p>Treść slotu głównego</p>
</x-layout>

				
			

2. Sloty a Livewire

Livewire nie wprowadza własnych, osobnych slotów, lecz bazuje na standardowym mechanizmie Blade.
Gdy tworzymy komponent Livewire, jego kod Blade może zawierać sloty (tak samo, jak każdy plik Blade), ale nie będzie to mechanizm dedykowany wyłącznie Livewire. Na przykład:

				
					
<div class="modal">
    <div class="modal-header">
        {{ $title }}
    </div>
    <div class="modal-body">
        {{ $slot }}
    </div>
    <div class="modal-footer">
        <button wire:click="cancel">Anuluj</button>
        <button wire:click="confirm">Potwierdź</button>
    </div>
</div>

				
			

Tu widzimy nazwany slot $title oraz domyślny $slot. Następnie w miejscu, gdzie umieszczamy nasz komponent, możemy wypełnić te sloty:

				
					<livewire:modal-wrapper>
    <x-slot name="title">
        Usuwanie zadania
    </x-slot>
    
    Czy na pewno chcesz usunąć to zadanie?
</livewire:modal-wrapper>

				
			

Takie podejście pozwala dość elastycznie sterować zawartością modala bez przepisywania kodu HTML.

Re-używalne komponenty: przykład Modal

1. Scenariusz

Załóżmy, że chcemy stworzyć uniwersalny komponent Modal, który możemy wykorzystywać w różnych miejscach aplikacji. Czasem ma on służyć do potwierdzania usunięcia rekordu, a czasem do wyświetlenia innej treści.

2. Tworzenie komponentu

Zaczynamy od wygenerowania komponentu:

				
					php artisan make:livewire Modal

				
			

Pojawią się dwa pliki:

  1. app/Http/Livewire/Modal.php – klasa komponentu.
  2. resources/views/livewire/modal.blade.php – widok Blade.

Klasa: Modal.php

				
					namespace App\Http\Livewire;

use Livewire\Component;

class Modal extends Component
{
    // Parametry modyfikujące zachowanie modala
    public $show = false;     // Decyduje, czy modal jest aktualnie wyświetlony
    public $title = 'Uwaga';  // Tytuł modala, domyślnie "Uwaga"
    public $message = '';     // Treść w modalu

    public function mount($title = null, $message = null)
    {
        if ($title) {
            $this->title = $title;
        }
        if ($message) {
            $this->message = $message;
        }
    }

    public function showModal()
    {
        $this->show = true;
    }

    public function hideModal()
    {
        $this->show = false;
    }

    // Zdarzenie potwierdzenia akcji
    public function confirm()
    {
        // Możemy np. wyemitować zdarzenie do rodzica z informacją,
        // że akcja została potwierdzona
        $this->emit('modalConfirmed');

        $this->hideModal();
    }

    public function render()
    {
        return view('livewire.modal');
    }
}

				
			

Kilka uwag do powyższego:

  • $show kontroluje, czy modal jest widoczny czy nie. Dzięki temu możemy manipulować tym stanem w Livewire, bez JavaScriptu.
  • W mount($title, $message) umożliwiamy przekazywanie parametrów do wstępnego ustawienia tytułu i treści.
  • W confirm() emitujemy zdarzenie modalConfirmed, by na zewnątrz ktoś mógł się dowiedzieć o akcji potwierdzenia.

Widok: modal.blade.php

				
					<div>
    @if($show)
        
        <div class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-75">
            <div class="bg-white p-4 rounded shadow-lg">
                <h2 class="text-xl font-bold mb-2">{{ $title }}</h2>

                
                @if($message)
                    <p>{{ $message }}</p>
                @endif

                
                <div>
                    {{ $slot }}
                </div>

                <div class="mt-4 flex justify-end">
                    <button wire:click="hideModal" class="mr-2">Anuluj</button>
                    <button wire:click="confirm">Potwierdź</button>
                </div>
            </div>
        </div>
    @endif
</div>

				
			
  • Gdy $show jest true, wyświetlamy warstwę zasłaniającą i okno modala.
  • Zawartość modala może być częściowo wypełniona przez parametry ($title, $message), a częściowo przez slot ({{ $slot }}).

3. Zagnieżdżanie komponentu Modal

Teraz możemy użyć Modal w innym komponencie, np. TaskList, by potwierdzać usunięcie zadania.

Przykład: TaskList z integracją modala

				
					namespace App\Http\Livewire;

use App\Models\Task;
use Livewire\Component;

class TaskList extends Component
{
    public $selectedTaskId;

    protected $listeners = ['modalConfirmed' => 'deleteTask'];

    public function confirmDelete($taskId)
    {
        $this->selectedTaskId = $taskId;
        // Emitujemy do komponentu Modal, by się pokazał
        $this->emitTo('modal', 'showModal');
    }

    public function deleteTask()
    {
        if ($this->selectedTaskId) {
            Task::find($this->selectedTaskId)?->delete();
            $this->selectedTaskId = null;
        }
    }

    public function render()
    {
        return view('livewire.task-list', [
            'tasks' => Task::all()
        ]);
    }
}

				
			

W powyższym kodzie:

  1. confirmDelete($taskId) – użytkownik klika na przycisk usunięcia, selectedTaskId się ustawia, a następnie wywołujemy emitTo('modal', 'showModal'), by pokazać okno modalne (o ile mamy przypisany alias modal w widoku).
  2. Gdy w modalu nastąpi confirm(), modal wyemituje modalConfirmed. TaskList odbierze to zdarzenie i uruchomi deleteTask().
  3. deleteTask() usunie rekord z bazy.

Widok: task-list.blade.php

				
					<div>
    <h2>Lista zadań</h2>
    <ul>
        @foreach($tasks as $task)
            <li>
                {{ $task->name }}
                <button wire:click="confirmDelete({{ $task->id }})">Usuń</button>
            </li>
        @endforeach
    </ul>

    
    <livewire:modal
        :title="'Potwierdzenie usunięcia'"
        :message="'Czy na pewno chcesz usunąć zadanie?'"
        wire:key="modal"
/>
</div>

				
			

Dzięki wire:key="modal" mamy pewność, że odwołujemy się do konkretnego wystąpienia komponentu Modal. Gdy wywołamy emitTo('modal', 'showModal'), Livewire zlokalizuje ten właśnie komponent i uruchomi metodę showModal().

Uwaga: Aby skorzystać z emitTo('modal', 'showModal'), komponent Modal musi mieć nazwę aliasu modal zarejestrowaną np. w pliku app/Providers/AppServiceProvider.php lub innym miejscu, gdzie definiujemy aliasy Livewire.
Możemy zrobić to np. w routes/web.php:

				
					use App\Http\Livewire\Modal;

Livewire::component('modal', Modal::class);

				
			

Dzięki temu <livewire:modal ...> jest interpretowane jako Modal::class.

Ćwiczenia

  1. Stworzyć komponent Modal – tak jak w przykładzie powyżej:

    • Dodaj metody showModal() i hideModal().
    • Udostępnij parametry title i message.
    • Dodaj slot w widoku, aby można było wypełnić modal własną treścią.
  2. Zagnieździć komponent Modal w innym komponencie – np. w TaskList:

    • Wywołaj emitTo('modal', 'showModal') przy kliknięciu przycisku „Usuń zadanie”.
    • Przekazuj do modala parametry (np. inny tytuł, treść).
  3. Obsługa eventów – Po kliknięciu „Potwierdź” w modalu:

    • Wyemituj zdarzenie modalConfirmed, aby komponent nadrzędny wykonał właściwą akcję (np. usunięcie zadania).
  4. (Opcjonalnie) Rozbudować modal o dodatkowe parametry lub sloty:

    • Możesz chcieć przekazać np. przycisk potwierdzenia w innym stylu, inny komunikat itd.
    • Możesz dodać wire:loading w modalu, aby zasygnalizować użytkownikowi, że trwa przetwarzanie.

Ćwiczenie 1: Stworzyć komponent Modal

Celem jest stworzenie prostego komponentu Livewire o nazwie Modal, który:

  1. Posiada metody do pokazywania i chowania modala (showModal(), hideModal()).
  2. Udostępnia publiczne właściwości, np. title, message.
  3. Pozwala wypełnić wnętrze modalnego okna przez slot (np. dodatkowym tekstem, formularzem).

Krok 1. Wygeneruj komponent

				
					php artisan make:livewire Modal

				
			

W efekcie otrzymasz plik klasy komponentu app/Http/Livewire/Modal.php oraz widok resources/views/livewire/modal.blade.php.

Krok 2. Uzupełnij klasę Modal.php

Poniżej przykładowa implementacja:

				
					<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Modal extends Component
{
    // Określa, czy modal ma być aktualnie widoczny
    public $show = false;

    // Tytuł modala
    public $title = 'Uwaga';

    // Dodatkowa treść, np. potwierdzenie
    public $message = '';

    /**
     * Metoda mount - wywoływana przy inicjalizacji komponentu.
     * Możemy tu ustawić wartości początkowe, jeśli zostaną przekazane z zewnątrz.
     */
    public function mount($title = null, $message = null)
    {
        if ($title) {
            $this->title = $title;
        }
        if ($message) {
            $this->message = $message;
        }
    }

    /**
     * Pokazuje modal
     */
    public function showModal()
    {
        $this->show = true;
    }

    /**
     * Chowa modal
     */
    public function hideModal()
    {
        $this->show = false;
    }

    /**
     * Akcja wykonywana przy potwierdzeniu
     */
    public function confirm()
    {
        // Przykład: emitujemy zdarzenie do innego komponentu
        $this->emit('modalConfirmed');
        $this->hideModal();
    }

    public function render()
    {
        return view('livewire.modal');
    }
}

				
			

Krok 3. Uzupełnij widok modal.blade.php

Przykładowa zawartość:

				
					
<div>
    @if($show)
        <div class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-75">
            
            <div class="bg-white p-6 rounded shadow-md w-full max-w-md">
                <h2 class="text-xl font-bold mb-4">
                    {{ $title }}
                </h2>

                @if($message)
                    <p class="mb-4">
                        {{ $message }}
                    </p>
                @endif

                
                <div class="mb-4">
                    {{ $slot }}
                </div>

                
                <div class="flex justify-end space-x-2">
                    <button wire:click="hideModal"
                            class="bg-gray-300 px-4 py-2 rounded">
                        Anuluj
                    </button>
                    <button wire:click="confirm"
                            class="bg-blue-500 text-white px-4 py-2 rounded">
                        Potwierdź
                    </button>
                </div>
            </div>
        </div>
    @endif
</div>

				
			

Gotowe! Masz już komponent Modal, który można gdzieś wstawić i kontrolować jego widoczność za pomocą metod showModal() i hideModal(). Slot pozwala wypełnić modal własną treścią w momencie użycia.

Ćwiczenie 2: Zagnieżdzić komponent Modal w innym komponencie

Załóżmy, że mamy komponent TaskList, który wyświetla listę zadań. Chcemy, by po kliknięciu w przycisk „Usuń” wyświetlało się okno Modal z pytaniem o potwierdzenie.

Krok 1. Dopisz alias modal (opcjonalne)

Aby łatwiej korzystać z Modal, możemy w pliku routes/web.php (lub AppServiceProvider) zadeklarować:

				
					use App\Http\Livewire\Modal;
use Livewire\Livewire;

Livewire::component('modal', Modal::class);

				
			

Dzięki temu <livewire:modal /> będzie rozpoznawane jako Modal::class.

Krok 2. W komponencie TaskList zaimplementuj wywołanie modala

Plik klasy TaskList.php

				
					<?php

namespace App\Http\Livewire;

use App\Models\Task;
use Livewire\Component;

class TaskList extends Component
{
    public $selectedTaskId;

    // Nasłuchujemy zdarzenia, które wywoła modal
    protected $listeners = ['modalConfirmed' => 'deleteTask'];

    /**
     * Po kliknięciu „Usuń”, ustawiamy ID zadania i emitujemy do Modal, żeby się pokazał.
     */
    public function confirmDelete($taskId)
    {
        $this->selectedTaskId = $taskId;

        // Kierujemy zdarzenie bezpośrednio do komponentu "modal"
        $this->emitTo('modal', 'showModal');
    }

    /**
     * Wywołane po potwierdzeniu w modalu (modalConfirmed).
     */
    public function deleteTask()
    {
        if ($this->selectedTaskId) {
            Task::find($this->selectedTaskId)?->delete();
            $this->selectedTaskId = null;
        }
    }

    public function render()
    {
        return view('livewire.task-list', [
            'tasks' => Task::all()
        ]);
    }
}

				
			

Wyjaśnienia:

  • confirmDelete($taskId) – metoda uruchamiana np. po kliknięciu przycisku „Usuń”. Ustawia zmienną selectedTaskId, a następnie emitTo('modal', 'showModal'), co zasygnalizuje naszemu komponentowi Modal, żeby się pokazał.
  • deleteTask() – zostanie wywołane wtedy, gdy w Modal klikniemy przycisk „Potwierdź” (bo w Modal::confirm() mamy $this->emit('modalConfirmed')).

Plik widoku task-list.blade.php

				
					<div>
    <h2>Lista zadań</h2>
    <ul>
        @foreach($tasks as $task)
            <li class="mb-2">
                {{ $task->name }}
                <button wire:click="confirmDelete({{ $task->id }})"
                        class="bg-red-500 text-white px-2 py-1 rounded">
                    Usuń
                </button>
            </li>
        @endforeach
    </ul>

    
    <livewire:modal
        :title="'Potwierdzenie usunięcia'"
        :message="'Czy na pewno chcesz usunąć zadanie?'"
        wire:key="delete-modal"
    />
</div>

				
			

Gotowe! Teraz po kliknięciu przycisku „Usuń” w TaskList, modal zostanie wywołany, a gdy w nim klikniesz „Potwierdź” – nastąpi usunięcie zadania z bazy.

Ćwiczenie 3: Obsługa eventów

Z poprzednich kroków już właściwie mamy implementację obsługi eventów w Modal. Kluczowe elementy to:

  1. W Modal:
				
					public function confirm()
{
    $this->emit('modalConfirmed');
    $this->hideModal();
}

				
			
  1. W TaskList:
				
					protected $listeners = ['modalConfirmed' => 'deleteTask'];

				
			

Dzięki temu, gdy Modal wyemituje modalConfirmed, w TaskList zostanie wywołana metoda deleteTask().

Tym samym potwierdzamy, że całe przepływy zdarzeń już działają – klikasz w Modal, a akcja jest wykonywana w TaskList.

Gotowe! Teraz po kliknięciu przycisku „Usuń” w TaskList, modal zostanie wywołany, a gdy w nim klikniesz „Potwierdź” – nastąpi usunięcie zadania z bazy.

Ćwiczenie 4 (Opcjonalne): Rozszerzyć modal o dodatkowe parametry / sloty

Możesz wzbogacić swój modal o jeszcze większą elastyczność:

  1. Dodaj slot z innym przyciskiem potwierdzenia
    Jeśli chcesz, by w różnych kontekstach nazwa przycisku potwierdzenia była inna, możesz przygotować dodatkowy slot. Przykład:

				
					
<div class="mt-4">
    
    {{ $buttons ?? '' }}
</div>

				
			

W momencie osadzania modala:

				
					<livewire:modal :title="'Edycja zadania'">
    <x-slot name="buttons">
        <button wire:click="cancelEdit" class="bg-gray-300 px-2 py-1">Anuluj</button>
        <button wire:click="saveEdit" class="bg-green-500 text-white px-2 py-1">Zapisz</button>
    </x-slot>
</livewire:modal>

				
			
  1. Wyspecjalizowane eventy
    Zamiast jednego eventu modalConfirmed, możesz mieć np. modalActionAccepted i modalActionRejected. Wtedy w zależności od kontekstu – potwierdzenie wywołuje inną akcję w komponencie nadrzędnym, a rezygnacja inną.

  2. wire:loading
    Jeśli w confirm() uruchamiasz czasochłonny proces, można dodać wire:loading do przycisku, by zasygnalizować użytkownikowi, że coś się dzieje:

				
					<button wire:click="confirm" wire:loading.attr="disabled">
    Potwierdź
</button>
<div wire:loading>
    Przetwarzanie...
</div>

				
			

Podsumowanie

Powyższe ćwiczenia pokazują, jak tworzyć komponenty wielokrotnego użytku (w tym przypadku Modal), jak przekazywać do nich parametry (title, message) oraz jak łączyć je z innymi komponentami za pomocą eventów i komunikacji (emit, emitTo). W dużej mierze bazują one na wcześniejszych artykułach, ale w bardziej praktycznej formie.

Najważniejsze kroki, które warto zapamiętać:

  1. Generowanie komponentu – php artisan make:livewire NazwaKomponentu.
  2. Publiczne właściwości i metoda mount() – służą do inicjalizacji i odbierania parametrów wejściowych.
  3. Metoda render() – definiuje, jak ma wyglądać widok i co do niego przekazujemy.
  4. emit() i emitTo() – umożliwiają komunikację między komponentami (np. z modala do listy zadań).
  5. Sloty w Blade – pozwalają zagnieździć w komponencie część widoku, która może różnić się w zależności od kontekstu.

Dzięki temu możesz tworzyć naprawdę elastyczne, zagnieżdżone i w pełni re-używalne komponenty w ekosystemie Laravel + Livewire. Jeśli w Twojej aplikacji pojawią się inne, bardziej skomplikowane przypadki (np. komponent tabów, panel boczny itp.), możesz śmiało wykorzystać te same wzorce. Powodzenia w dalszym rozwijaniu aplikacji!