Содержание
Зачастую мы используем выпадающие списки в сочетании с другими элементами HTML, чтобы сделать наш веб-сайт интерактивным.
Одним из видов взаимодействия является взаимосвязь между несколькими выпадающими списками. Это происходит, когда изменение выбранного варианта одного выпадающего списка влияет на выбор, предоставленный для другого выпадающего списка. В нашей последней статье мы создали динамические, взаимосвязанные выпадающие списки. Эти выпадающие списки динамически инстанциировались, так как они все использовали один компонент Livewire в качестве шаблона (названный компонентом filter). Затем, чтобы обеспечить взаимосвязь между выпадающими списками, мы создали другой компонент с именем filter-section, который управляет данными и зависимостью данных для каждого экземпляра выпадающего списка filter.
Другой вид взаимодействия включает в себя влияние выпадающих списков на другие элементы HTML, например, фильтрацию данных в таблице.
Конечно, учитывая характер динамически созданных элементов, нам нужно иметь возможность уникально идентифицировать и отслеживать каждый динамически созданный выпадающий список. Более того, если наш элемент таблицы находится в отдельном компоненте Livewire, нам нужно найти способ обмена данными между каждым выпадающим списком filter и компонентом таблицы table.
Сегодня мы легко разберемся с обменом данными с помощью Livewire Modelable и событий, генерируемых с клиентской стороны.
Приступим!
Настройка динамических выпадающих списков
Допустим, у нас есть компонент filter-section, который управляет несколькими выпадающими списками. У него есть публичный атрибут $filters, который содержит массив данных, которые мы будем использовать для инициализации нескольких выпадающих списков:
<!-- resources/livewire/filter-section.blade.php -->
<div>
<button>Apply</button>
@foreach( $filters as $key=> $filter )
<livewire:filter
:key="$key.now()"
:label="$filter['label']"
:options="$filter['options']"
/>
@endforeach
</div>
Однако обратите внимание, что каждый элемент инициализирует другой компонент Livewire с именем filter. Этот компонент содержит шаблон, который мы используем для создания наших выпадающих списков:
<!-- resources/views/livewire/filter.blade.php -->
<div>
<!-- Show the label of the filter -->
<label>{{ $label }}</label>
<!-- Filter -->
<select wire:model="value">
@foreach( $options as $option )
<option value="{{ $option['id'] }}">{{ $option['name'] }}</option>
@endforeach
</select>
</div>
Предположим, что массив $filters содержит три набора данных для выпадающих списков: ”Тип предложения”, “Тип недвижимости” и “Подтип недвижимости”.Мы должны получить вид, как показано ниже:
Мы собираемся использовать эти выпадающие списки для отображения отфильтрованных результатов в таблице. Первое, что нам нужно сделать, это получить выбранный вариант для каждого выпадающего списка.
Как именно мы это сделаем?
Уникальная идентификация выпадающих списков
В нашей настройке у нас есть родительский компонент filter-section, который динамически инициализирует несколько дочерних компонентов filter с помощью массива данных. Каждый экземпляр компонента filter содержит элемент выпадающего списка.
Если мы хотим получить выбранный вариант для каждого выпадающего списка, нам, конечно, нужно уникально идентифицировать каждый из них.Итак, сначала убедитесь, что массив $filters включает уникальное ключевое значение для каждого набора данных выпадающего списка. Затем нам нужен атрибут “value”, который будет представлять выбранный вариант для каждого выпадающего списка:
public function mount()
{
$this->filters = [
'offer_type'=>[ // Unique key
'label'=> 'OFFER TYPE',
'options'=> OfferType::all()->toArray(),
'value' => 0 // Value attr
],
'property_type'=>[ // Unique key
'label'=>'PROPERTY TYPE',
'options'=> PropertyType::all()->toArray(),
'value' => 0 // Value attr
],
'sub_property_type'=>[ // Unique key
'label'=>'SUB PROPERTY TYPE',
'options'=> SubPropertyType::all()->toArray(),
'value'=>0 // Value attr
]
];
}
С такой настройкой мы можем обратиться к выбранному значению каждого выпадающего списка через атрибут $filters.[drop_down_key].value.
Теперь, когда у нас есть имя атрибута для идентификации значения каждого выпадающего списка, следующим шагом является привязка каждого атрибута к соответствующему элементу выпадающего списка в нашем представлении. В Livewire мы связываем атрибуты сервера с элементами в шаблоне с помощью директивы wire:model:
<!-- resources/livewire/filter-section.blade.php -->
@foreach( $filters as $key=> $filter )
<livewire:filter
...
+ :wire:model="'filters.'.$key.'.value'"
/>
@endforeach
Обычно так мы бы связали атрибуты с элементами HTML. Но обратите внимание, что у компонента filter-section есть прямой доступ только к дочерним экземплярам компонента filter, а не к элементам выпадающего списка, которые мы хотим привязать.
Этот синтаксис не сработает сам по себе. Потому что, в конечном итоге, как Livewire узнает, к какому элементу в шаблоне компонента filter следует привязать атрибут?
Ввод Modelable: Связь атрибутов родителя и дочерних элементов
Livewire предоставляет нам возможность использовать его атрибут Modelable. Он позволяет нам связать атрибут родительского элемента wire:model с любым атрибутом дочерних компонентов и, в конечном итоге, с элементами HTML этого дочернего компонента.
С его помощью мы можем синхронизировать атрибуты родительского компонента с атрибутами дочерних компонентов!
Применение этой директивы - это всего два шага. Первый шаг мы уже выполнили выше, который заключается в привязке конкретного атрибута родительского компонента filter-section к экземпляру дочернего компонента filter.
Затем нам нужно указать компоненту filter, куда следует привязать переданный атрибут. Для этого мы можем применить атрибут “Modelable” к атрибуту, который мы хотим связать. Поскольку наш компонент filter имеет атрибут $value, который связан с элементом выпадающего списка, мы хотим явно применить этот атрибут:
/* app/Livewire/Filter.php */
use Livewire\Attributes\Modelable;
class Filter extends Component
{
#[Modelable]
public $value = '';
И вот и все! Теперь, когда мы применили атрибут Modelable к $value и связали его с атрибутом $filters.[drop_down_key].value родителя, все изменения в $value также будут отражены в соответствующем $filters.[drop_down_key].value, что дает компоненту filter-section доступ к значениям выпадающих списков, найденным в каждом экземпляре дочерних компонентов filter.
Здорово, верно?
Давайте протестируем это с помощью немного кода на Alpine.js и журналирования в консоль. Из родительского компонента filter-section у нас есть кнопка “Apply”. Когда пользователь нажимает на эту кнопку, мы можем зарегистрировать значение конкретного выпадающего списка:
<button
x-on:click="console.log( $wire.filters.sub_property_type.value )"
>
И… вуаля! Нажатие кнопки “Apply” показывает нам значение нашего целевого выпадающего списка!
Поделиться данными снаружи
Теперь, когда у нас есть способ получить каждое выбранное значение выпадающего списка, давайте завершим это, передав их в другой компонент - в наш компонент table.
Компонент table будет использовать трейт WithPagination от Livewire для отображения строк наших данных. И для возможности фильтрации мы также добавим публичный атрибут $filterList, который мы передадим в запрос пагинации:
/* app/Livewire/Table.php */
use Livewire\WithPagination;
class Table extends Component
{
use WithPagination;
public $filters = [];
public function render()
{
return view('livewire.property-table',[
'rows'=>Property::filter( $this->filters )->paginate(10)
]);
}
}
Затем, с данными, переданными через переменную $rows в метод render(), мы создаем представление, включая данные и ссылки на пагинацию:
<!-- resources/views/livewire/table.blade.php -->
<table>
<thead>
<tr>
<th class="bg-blue-100 border text-left px-8 py-4">Name</th>
<th class="bg-blue-100 border text-left px-8 py-4">Price</th>
</tr>
</thead>
<tbody>
@foreach( $rows as $row )
<tr>
<td>{{$row['name']}}</td>
<td>{{$row['price']}}</td>
</tr>
@endforeach
</tbody>
{{ $rows->links() }}
</table>
Теперь, если мы объявим этот компонент таблицы на нашей главной странице, мы, наконец, получим полную настройку, как показано ниже:
Последний вопрос в этой статье: Как передать значения выбранных вариантов выпадающего списка в компонент таблицы table?
События, инициированные клиентом
Для передачи данных между компонентами в Livewire мы обычно отправляем события. Компоненты Livewire могут прослушивать такие события и реагировать на них соответственно.
Чтобы мгновенно и эффективно инициировать событие, мы можем создать его в нашем шаблоне Blade.
Когда пользователь нажимает кнопку ”Применить” в компоненте filter-section, мы хотим взять данные выбранных выпадающих списков, к которым у нас есть доступ, и передать их в компонент таблицы.
Для этого мы можем создать событие, содержащее выбранное значение каждого выпадающего списка, нажав кнопку:
<!-- resources/views/livewire/filter-section.blade.php -->
<button
wire:click="$dispatch('apply-filter', {
offer_type_id: $wire.filters.offer_type.value,
property_type_id: $wire.filters.property_type.value,
sub_property_type_id: $wire.filters.sub_property_type.value,
})"
Применить</button>
Поскольку это событие apply-filter доступно на всей странице, мы можем прослушивать его из вида нашего компонента таблицы (или из любого другого компонента, на самом деле) и реагировать на него.
Мы можем взять данные выбранных выпадающих списков из event.detail и напрямую обновить переменную $filtersList в нашем компоненте таблицы:
<script>
window.addEventListener('apply-filter', event => {
console.log('получено событие из компонента filter-section в компонент таблицы!', event.detail);
+ @this.set('filtersList', event.detail);
});
</script>
И вот и все! Мы поделились выбранными значениями выпадающих списков с компонентом таблицы!
После того как Livewire обновит атрибут $filtersList на сервере с помощью @this.set(), Livewire вызовет метод render() компонента таблицы из-за обновления его общедоступного атрибута.
На этот раз $filtersList будет содержать данные, выбранные пользователем, и будет использоваться для фильтрации строк в нашей таблице! При нажатии кнопки ”Применить” происходит изменение строк данных в таблице.
