2.7. Вложенные компоненты
Livewire позволяет вкладывать дополнительные Livewire-компоненты внутрь родительского компонента. Эта функция чрезвычайно мощная, так как позволяет повторно использовать и инкапсулировать логику внутри Livewire-компонентов, которые используются в разных частях вашего приложения.
Прежде чем вынести часть вашего шаблона в вложенный Livewire-компонент, задайте себе вопрос: нужно ли этому компоненту быть "живым"? Если нет, мы рекомендуем создать простой Blade-компонент. Создавайте Livewire-компонент только в том случае, если компонент выигрывает от динамической природы Livewire или если есть прямая выгода в производительности.
Обратитесь к нашему подробному техническому исследованию вложенности Livewire-компонентов, чтобы узнать больше о производительности, особенностях использования и ограничениях вложенных компонентов Livewire.
1. Вкладывание компонента
Чтобы вложить Livewire-компонент в родительский компонент, просто включите его в Blade-шаблон родительского компонента. Ниже приведён пример родительского компонента Dashboard, содержащего вложенный компонент TodoList:
<?php namespace App\Livewire; use Livewire\Component; class Dashboard extends Component{ public function render() { return view('livewire.dashboard'); }}
<div> <h1>Dashboard</h1> <livewire:todo-list /> </div>
При начальном рендере этой страницы компонент Dashboard встретит <livewire:todo-list /> и отрендерит его на месте. При последующем сетевом запросе к Dashboard вложенный компонент todo-list пропустит рендеринг, так как теперь он является независимым компонентом на странице. Для получения дополнительной информации о технических концепциях, связанных с вложенностью и рендерингом, ознакомьтесь с нашей документацией о том, почему вложенные компоненты — это "острова".
Для получения дополнительной информации о синтаксисе рендеринга компонентов обратитесь к нашей документации по Рендерингу компонентов.
2. Передача пропсов дочерним компонентам
Передача данных из родительского компонента в дочерний компонент предельно проста. Фактически, это очень похоже на передачу пропсов в типичный Blade-компонент.
Например, давайте рассмотрим компонент TodoList, который передаёт коллекцию $todos в дочерний компонент под названием TodoCount:
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component; class TodoList extends Component{ public function render() { return view('livewire.todo-list', [ 'todos' => Auth::user()->todos, ]); }}
<div> <livewire:todo-count :todos="$todos" /> <!-- ... --></div>
Как видите, мы передаём $todos в todo-count с помощью синтаксиса: :todos="$todos".
Теперь, когда $todos был передан в дочерний компонент, вы можете получить эти данные через метод mount() дочернего компонента:
<?php namespace App\Livewire; use Livewire\Component;use App\Models\Todo; class TodoCount extends Component{ public $todos; public function mount($todos) { $this->todos = $todos; } public function render() { return view('livewire.todo-count', [ 'count' => $this->todos->count(), ]); }}
mount() как более короткую альтернативу
Если метод mount() в приведённом выше примере кажется вам избыточным шаблонным кодом, его можно опустить, если имена свойства и параметра совпадают:
public $todos;
2.1. Передача статических пропсов
В предыдущем примере мы передали пропсы в наш дочерний компонент, используя синтаксис динамических пропсов Livewire, который поддерживает PHP-выражения, как показано ниже:
<livewire:todo-count :todos="$todos" />
Однако иногда вы можете захотеть передать компоненту простое статическое значение, такое как строка. В этих случаях вы можете опустить двоеточие в начале выражения:
<livewire:todo-count :todos="$todos" label="Todo Count:" />
Логические значения могут быть переданы компонентам путём указания только ключа. Например, чтобы передать переменную $inline со значением true в компонент, мы можем просто добавить inline в тег компонента:
<livewire:todo-count :todos="$todos" inline />
2.2. Сокращённый синтаксис атрибутов
При передаче PHP-переменных в компонент имя переменной и имя пропса часто совпадают. Чтобы избежать повторного написания имени, Livewire позволяет просто добавить двоеточие перед переменной:
-<livewire:todo-count :todos="$todos" /> +<livewire:todo-count :$todos />
3. Рендеринг дочерних компонентов в цикле
При рендеринге дочернего компонента внутри цикла вы должны указать уникальное значение key для каждой итерации.
Ключи компонентов используются Livewire для отслеживания каждого компонента при последующих рендерах, особенно если компонент уже был отрендерен или если несколько компонентов были переставлены на странице.
Вы можете указать ключ компонента, добавив пропс :key в дочерний компонент:
<div> <h1>Todos</h1> @foreach ($todos as $todo) <livewire:todo-item :$todo :key="$todo->id" /> @endforeach</div>
Как видите, у каждого дочернего компонента будет уникальный ключ, установленный на ID каждого $todo. Это гарантирует, что ключ будет уникальным и отслеживаться, если задачи будут переупорядочены.
Если вы использовали фронтенд-фреймворки, такие как Vue или Alpine, вы знакомы с добавлением ключа к вложенному элементу в цикле. Однако в этих фреймворках ключ не является обязательным, то есть элементы будут рендериться, но переупорядочивание может отслеживаться неправильно. Однако Livewire гораздо больше полагается на ключи и будет работать некорректно без них.
4. Реактивные пропсы
Разработчики, впервые работающие с Livewire, ожидают, что пропсы по умолчанию являются "реактивными". Другими словами, они предполагают, что когда родительский компонент изменяет значение пропса, передаваемого в дочерний компонент, дочерний компонент будет автоматически обновлён. Однако по умолчанию пропсы в Livewire не являются реактивными.
При использовании Livewire каждый компонент является островом. Это означает, что когда в родительском компоненте происходит обновление и отправляется сетевой запрос, на сервер передаётся только состояние родительского компонента для повторного рендеринга — состояние дочернего компонента не передаётся. Цель этого поведения — минимизировать объём данных, передаваемых между сервером и клиентом, делая обновления максимально производительными.
Однако, если вы хотите или вам нужно, чтобы пропс был реактивным, вы можете легко включить это поведение, используя параметр атрибута #[Reactive].
Например, ниже представлен шаблон родительского компонента TodoList. Внутри он отображает компонент TodoCount и передаёт в него текущий список задач:
<div> <h1>Todos:</h1> <livewire:todo-count :$todos /> <!-- ... --></div>
Теперь давайте добавим #[Reactive] к пропсу $todos в компоненте TodoCount. После этого любые задачи, добавленные или удалённые внутри родительского компонента, автоматически вызовут обновление в компоненте TodoCount:
<?php namespace App\Livewire; use Livewire\Attributes\Reactive;use Livewire\Component;use App\Models\Todo; class TodoCount extends Component{ #[Reactive] public $todos; public function render() { return view('livewire.todo-count', [ 'count' => $this->todos->count(), ]); }}
Реактивные свойства — это чрезвычайно мощная функция, делающая Livewire более похожим на фронтенд-библиотеки компонентов, такие как Vue и React. Однако важно понимать влияние этой функции на производительность и добавлять #[Reactive] только тогда, когда это имеет смысл в вашем конкретном случае.
5. Привязка к дочерним данным с помощью wire:model
Ещё один мощный способ для совместного использования состояния между родительским и дочерним компонентами — это использование wire:model напрямую на дочернем компоненте через возможность Livewire под названием Modelable.
Такое поведение часто необходимо, когда вы выносите элемент ввода в отдельный компонент Livewire, но при этом хотите по-прежнему получать доступ к его состоянию в родительском компоненте.
Ниже приведён пример родительского компонента TodoList, который содержит свойство $todo, отслеживающее текущее задание, которое пользователь собирается добавить:
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Todo; class TodoList extends Component{ public $todo = ''; public function add() { Todo::create([ 'content' => $this->pull('todo'), ]); } public function render() { return view('livewire.todo-list', [ 'todos' => Auth::user()->todos, ]); }}
Как видно в шаблоне TodoList, wire:model используется для привязки свойства $todo напрямую к вложенному компоненту TodoInput:
<div> <h1>Todos</h1> <livewire:todo-input wire:model="todo" /> <button wire:click="add">Add Todo</button> <div> @foreach ($todos as $todo) <livewire:todo-item :$todo :key="$todo->id" /> @endforeach </div></div>
Livewire предоставляет атрибут #[Modelable], который можно добавить к любому свойству дочернего компонента, чтобы сделать его доступным для привязки из родительского компонента.
Ниже представлен компонент TodoInput с атрибутом #[Modelable], добавленным над свойством $value, чтобы указать Livewire, что при использовании wire:model в родительском компоненте привязка должна происходить к этому свойству:
<?php namespace App\Livewire; use Livewire\Component;use Livewire\Attributes\Modelable; class TodoInput extends Component{ #[Modelable] public $value = ''; public function render() { return view('livewire.todo-input'); }}
<div> <input type="text" wire:model="value" ></div>
Теперь родительский компонент TodoList может обращаться с TodoInput как с любым другим элементом ввода и напрямую привязываться к его значению с помощью wire:model.
В настоящее время Livewire поддерживает только один атрибут #[Modelable], поэтому будет привязано только первое найденное свойство.
6. Прослушивание событий от дочерних компонентов
Ещё один мощный способ взаимодействия между родительским и дочерним компонентами — это система событий Livewire, которая позволяет отправлять события на сервере или клиенте, и эти события могут быть перехвачены другими компонентами.
В нашей полной документации по системе событий Livewire вы найдёте более подробную информацию о событиях, но ниже мы рассмотрим простой пример использования события для запуска обновления в родительском компоненте.
Рассмотрим компонент TodoList с функциональностью для отображения и удаления задач:
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Todo; class TodoList extends Component{ public function remove($todoId) { $todo = Todo::find($todoId); $this->authorize('delete', $todo); $todo->delete(); } public function render() { return view('livewire.todo-list', [ 'todos' => Auth::user()->todos, ]); }}
<div> @foreach ($todos as $todo) <livewire:todo-item :$todo :key="$todo->id" /> @endforeach</div>
Чтобы вызвать remove() из дочерних компонентов TodoItem, вы можете добавить прослушиватель события в TodoList с помощью атрибута #[On]:
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Todo;use Livewire\Attributes\On; class TodoList extends Component{ #[On('remove-todo')] public function remove($todoId) { $todo = Todo::find($todoId); $this->authorize('delete', $todo); $todo->delete(); } public function render() { return view('livewire.todo-list', [ 'todos' => Auth::user()->todos, ]); }}