Я имею опыт разработки React и WordPress и пытаюсь разобраться в компонентах Livewire с помощью Laravel Volt. Моя дилемма заключается в том, что я пытаюсь обобщить больше компонентов формы, но сталкиваюсь с трудностями при сохранении синхронизации состояния.
Вот мой компонент формы:
<?php
use App\Models\MyModel;
use Illuminate\Database\Eloquent\Collection;
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
new class extends Component {
#[Validate('required|string|max:256')]
public string $description;
public ?MyModel $editing = null;
public function submit(): void
{
$validated = $this->validate();
$this->dispatch('my-model-form-submitted', $validated);
$this->description = '';
}
public function mount(): void
{
if (isset($editing)) {
$this->editing = $editing;
$this->description = $this->editing->description;
} else {
$this->description = '';
}
}
};
?>
<form wire:submit.prevent = "submit">
<livewire:form-input :label = "__('Description')" id = "description" wire:model = "description"
placeholder = "{{ __('Describe the thing') }}" :error = "$errors->get('description')" />
<livewire:form-input :label = "__('Description a second time')" id = "description2" wire:model = "description"
placeholder = "{{ __('Ideally in sync with the same value as the first input') }}" :error = "$errors->get('description')" />
<div>
{{ $editing }}
<x-primary-button>{{ $editing ? __('Update') : __('Save') }}</x-primary-button>
</div>
</form>
Вот компонентviews/livewire/form-input.blade.php (в основном ввод с меткой и сообщением об ошибке):
<?php
use Livewire\Attributes\Modelable;
new class extends \Livewire\Volt\Component {
public string $id;
public string $label;
#[Modelable]
public string $value = '';
public string $placeholder;
public array $error;
public function mount(string $id, string $label, string $placeholder, array $error = []): void
{
$this->id = $id;
$this->label = $label;
$this->placeholder = $placeholder;
$this->error = $error;
}
};
?>
<x-form-group>
<x-input-label for = "{{ $id }}">{{ $label }}</x-input-label>
<x-input-text type = "{{ $type ?? 'text' }}" id = "{{ $id }}" wire:model = "value"
placeholder = "{{ $placeholder }}" />
<x-input-error :messages = "$error" />
</x-form-group>
а вот представления/компоненты/input-text.blade.php (только элемент ввода):
<input
{{ $attributes->merge(['class' => ' class = "block w-full px-4 py-2 border-gray-300 text-slate-900 dark:text-white focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"']) }}>
Я чувствую, что упускаю ключевую деталь, поскольку два ввода в форме не синхронизируются, загрузка модели для редактирования не устанавливает значение входных данных, а отправка формы только отключает ее, не отправляя событие или сброс значения описания.
Обновлять: Я разобрался с дилеммой синхронизации двух входов. Теперь я вижу, что состояние обновляется на сервере только при выполнении действия. https://livewire.laravel.com/docs/wire-model но я все еще озадачен загрузкой для редактирования и отсутствием значений во время отправки.
Во-первых, давайте посмотрим на вашу функцию монтирования
public function mount(): void
{
if (isset($editing)) {
Мы проверяем, установлена ли переменная $editing, но никогда не пытаемся ее установить. Вы можете сделать это, указав, что это параметр, который мы принимаем в функции монтирования.
Ниже приведена фиксированная версия. Я переименовал MyModel в модель Post, чтобы это имело больше смысла.
public function mount(?Post $post = null): void
{
if (isset($post)) {
$this->post = $post;
Метод mount не является конструктором класса, но, по сути, он ведет себя именно так. Это может быть концепция, с которой вы знакомы по реакции.
Это еще не полное решение.
Дальнейшие улучшения
Теперь, чтобы закончить это, мы собираемся делать что-то «путем Laravel», а также некоторые проблемы, связанные с безопасностью, о которых нам следует знать, и лучший способ сделать что-то для повышения производительности.
#[Validate('required|string|min:1|max:256')]
public string $description;
public Post $post;
public function submit(): void
{
$validated = $this->validate();
/**
* You likely just gave me psuedo but we should actually create a
* model here usually */
$this->dispatch('my-post-form-submitted', $validated);
$this->description = '';
}
public function mount(?Post $post = null): void
{
if (isset($post)) {
$this->post = $post;
$this->description = $this->post->description;
}
}
Раньше наша функция монтирования присваивала переменной описания значение пустой строки. Поскольку он был инициализирован, он всегда проходил «обязательное» правило проверки.
Мы также можем установить для Post значение null в методе монтирования. Используя ?Post $post = null, мы указали, что параметр $post должен быть экземпляром класса Post. Мы также присвоили ему значение по умолчанию, равное нулю, которое будет использоваться, если при рендеринге компонента не было передано ни одного сообщения.
Чтобы удвоить уверенность в том, что пользователь ввел что-то в качестве описания, было установлено минимальное правило проверки, указав не менее 1 символа.
Функция монтирования имеет типизированный параметр $post. Если в этой форме есть маршрут и это полностраничный компонент, это будет сделано именно так. Если эта форма является всего лишь дочерним компонентом и имеет родительский компонент, вам следует изменить ее, чтобы она принимала целое число, и загрузить связанную модель со строкой, аналогичной этой, внутри метода монтирования.
$this->post = Post::findOrFail($id);
У меня нет полных подробностей реализации, а есть только то, что было предоставлено, но имеющийся у меня контекст создает впечатление, что родительский компонент имеет копию модели Post и используется вертикально.
В большинстве случаев это делать не имеет смысла. Если родительскому элементу нужен экземпляр Post, отмените вложение этого компонента и просто поместите функциональность в его родительский элемент. Передача моделей вертикально через вложенные компоненты довольно затруднительна.
Проблемы безопасности
Livewire будет включать любые общедоступные свойства в запросы для отслеживания состояния. В некоторых случаях это может быть опасно, поскольку пользователь может изменить значения свойств с помощью модификатора общего доступа.
Например, если пользователь редактирует сообщение, а он должен редактировать только сообщения, принадлежащие ему, он может манипулировать свойством postId, если вы сохранили его в общедоступном свойстве, для редактирования сообщения, которое ему не принадлежит.
К счастью, Livewire умен и блокирует для нас свойство ID на моделях. Но помните об этом в будущем и взгляните на Проблемы безопасности собственности
Обработка отправки формы
Внутри вашей формы вы, похоже, излучаете свойство $description, которое предполагает, что оно передается вертикально между компонентами.
Это действительно неэффективно и опасно, поскольку все это делается на стороне клиента, и пользователь может манипулировать свойствами и их значениями.
Если вам нужно передать этот параметр вертикально, потому что он хочет разделить проблемы, я понимаю. В этом случае вы можете использовать Livewire Forms, поскольку они не имеют представления и просто содержат логику проверки и отправки формы.
Состояние и живое обновление
Вы можете настроить состояние так, чтобы оно практически проверялось, как только пользователь вводит ключ внутри одного из входов, который имеет привязку к свойству. Существует множество различных вариантов, но я рекомендую использовать wire:model.onblur , поскольку он будет запускать запрос только тогда, когда ввод теряет фокус, что гарантирует, что наше приложение не будет слишком громким и не отправит больше данных, чем нам нужно.
И последнее замечание, но я не буду вдаваться в подробности. Входные компоненты не обязательно должны быть компонентами Livewire, и вы можете просто использовать Компоненты Blade для того, чего вы сейчас пытаетесь добиться от них. Livewire просто не соответствует вашим текущим требованиям.
Лично я не использую Volt именно по этой причине. Я использую Livewire без Volt, так что у меня есть один файл, который является блейд-файлом/IE, который может видеть клиент, и класс компонента Livewire, который представляет собой логику на стороне сервера.
Большое спасибо за подробное объяснение. Есть ли какие-нибудь хорошие общие ресурсы для Volt и Livewire помимо документации Laravel? Я все еще пытаюсь разобраться в том, «что происходит на сервере» и «что происходит на клиенте».