Я был поклонником Laravel Livewire с момента его первого выпуска, и я много раз использовал его для создания отличных пользовательских интерфейсов для приложений. Одна из вещей, которую я часто делаю, — это создание извлеченных компонентов, в которые я могу передавать другие, например, чтобы у меня не было 20 разных модальных окон или слайдов.
В этом руководстве я расскажу, как можно создать компонент слайдера для ваших приложений TALL Stack, чтобы вы тоже могли реализовать этот подход.
Я предполагаю, что вы уже установили Laravel и Livewire и настроили базовое приложение. Я буду использовать Tailwind UI и Tailwind CSS для стиля, потому что понятия не имею, как что-либо проектировать.
Давайте начнем с создания универсального компонента в Livewire под названием SidePanel
, который будет содержать весь код и элементы управления, необходимые для открытия и закрытия панели. Приступим к сборке.
php artisan livewire:make SidePanel --test
Сначала нам нужно иметь доступное состояние, чтобы оно могло быть либо открытым, либо закрытым, что, я уверен, вы согласитесь, является жизненно важным аспектом боковой панели.
declare(strict_types=1);
namespace App\Http\Livewire;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View;
use Livewire\Component;
final class SidePanel extends Component
{
public bool $open = false;
public function render(): ViewContract
{
return View::make(
view: 'livewire.side-panel',
);
}
}
Здесь у нас есть стандартный компонент Livewire, настроенный на мой очень самоуверенный лад. Пока что у нас есть одно свойство, называемое open
, которое мы будем «запутывать» во внешнем интерфейсе с помощью AlpineJS. Нам нужна пара других свойств, чтобы мы могли отображать панель и не падать, если компонент не передан.
declare(strict_types=1);
namespace App\Http\Livewire;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View;
use Livewire\Component;
final class SidePanel extends Component
{
public bool $open = false;
public string $title = 'Default Panel';
public string $component = '';
public function render(): ViewContract
{
return View::make(
view: 'livewire.side-panel',
);
}
}
У нас есть заголовок по умолчанию и свойство component
, которое позволяет нам выбирать, какой компонент загружается. Таким образом, другие компоненты, взаимодействующие с ним, могут сказать ему, что загружать, и передать заголовок для отображения. Давайте взглянем на шаблон для этого компонента:
<section
x-data="{ open: @entangle('open') }"
@keydown.window.escape="open = false"
x-show="open"
x-cloak
class="relative z-10"
aria-labelledby="slide-over-title"
x-ref="dialog"
aria-modal="true"
>
<div
x-show="open"
x-cloak
x-transition:enter="ease-in-out duration-500"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in-out duration-500"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
></div>
<div class="fixed inset-0 overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
<div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10">
<div
x-show="open"
x-cloak
x-transition:enter="transform transition ease-in-out duration-500 sm:duration-700"
x-transition:enter-start="translate-x-full"
x-transition:enter-end="translate-x-0"
x-transition:leave="transform transition ease-in-out duration-500 sm:duration-700"
x-transition:leave-start="translate-x-0"
x-transition:leave-end="translate-x-full"
class="pointer-events-auto w-screen max-w-md"
@click.away="open = false"
>
<div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl">
<header class="px-4 sm:px-6">
<div class="flex items-start justify-between">
<h2 class="text-lg font-medium text-gray-900" id="slide-over-title">
Panel title
</h2>
<div class="ml-3 flex h-7 items-center">
<button
type="button"
class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
@click="open = false"
>
<span class="sr-only">Close panel</span>
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
</div>
</header>
<article class="relative mt-6 flex-1 px-4 sm:px-6">
@if ($component)
@livewire($component)
@else
<div class="absolute inset-0 px-4 sm:px-6">
<div class="h-full border-2 border-dashed border-gray-200" aria-hidden="true"></div>
</div>
@endif
</article>
</div>
</div>
</div>
</div>
</div>
</section>
Здесь у нас довольно много разметки, чтобы управлять тем, как он выглядит и ведет себя, используя @entangle
для связи между AlpineJS и Livewire. Мы проверяем, установлен ли component
, и, если нет, отображаем пустое состояние. Затем нам нужно вызвать это из других компонентов, отправив ему некоторые данные, чтобы мы могли выбрать компонент для загрузки и заголовок для установки. Это будет работать как с Livewire, так и с Alpine, но в моем примере я буду использовать другой компонент Livewire для запуска открытия панели.
$this->emit('openPanel', 'New Title', 'component.name.with-namespace');
Мы передаем три параметра, используя метод emit
. Во-первых, название события, которое мы запускаем. Во-вторых, название панели. Наконец, мы хотим передать сам компонент, как если бы мы загружали его с помощью директивы livewire в вашем представлении.
Теперь нам нужно попросить наш компонент боковой панели прослушивать это событие и получить метод, который будет обрабатывать логику обновления его свойств.
declare(strict_types=1);
namespace App\Http\Livewire;
use Illuminate\Contracts\View\View as ViewContract;
use Illuminate\Support\Facades\View;
use Livewire\Component;
final class SidePanel extends Component
{
public bool $open = false;
public string $title = 'Default Panel';
public string $component = '';
protected $listeners = [
'openPanel'
];
public function openPanel(string $title, string $component): void
{
$this->open = true;
$this->title = $title;
$this->component = $component;
}
public function render(): ViewContract
{
return View::make(
view: 'livewire.side-panel',
);
}
}
Поскольку слушатель принимает все необходимые параметры, а Alpine обрабатывает открытое и закрытое состояния, мы можем закрыть эту панель и заменить показанный компонент.
Этот подход позволил мне создать чистого пользователя интерфейсы, которые открывают отдельный компонент по мере необходимости. Кроме того, компонент, который мы передаем на панель, по-прежнему может использоваться как изолированный компонент для представления.
Как вы справляетесь с этими вариантами использования в своих проектах? Как вы думаете, что изменится в Livewire v3? Расскажите нам о своих мыслях в Твиттере.