Я работаю над сайтом Laravel 10. Используя документацию по адресу https://dev.to/koossaayy/laravel-livewire-multiple-selection-with-virtual-select-1f87, я пытаюсь сделать множественный выбор. Я сделал это с некоторыми изменениями в коде.
В компоненте:
<?php
namespace App\Livewire\Admin;
use App\Models\Permission;
use Illuminate\Support\Str;
use Livewire\Component;
class UsersPermissionsEditor extends Component
{
public $selectedPermissions= [];
public $permissionsListing= [];
public function render()
{
$this->permissionsListing = Permission::query()->get()/*->pluck('name', 'id')*/
->map(function ($selectedPermissionItem) {
return ['id' => $selectedPermissionItem->id, 'label' => Str::replace('_', ' ', Str::title($selectedPermissionItem->name))];
})
->toArray();
return view('livewire.admin.users-permissions-editor');
}
}
и в Блейде:
<div class = "admin_page_container" id = "users_permissions_editor_admin_page_container">
<div class = "editor_listing_wrapper_bix_width" x-data = "adminUsersPermissionsEditorComponent()"
x-init = "[onAdminUsersPermissionsEditorComponentInit() ]" x-cloak>
$selectedPermissions::{{ print_r($selectedPermissions, true) }}
<input id = "permissionsListing" name = "permissionsListing" value = "add permissionsListing TEST">
<script src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/virtual-select.min.js"
integrity = "sha256-Gsn2XyJGdUeHy0r4gaP1mJy1JkLiIWY6g6hJhV5UrIw = " crossorigin = "anonymous"></script>
<link rel = "stylesheet"
href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/virtual-select.min.css"
integrity = "sha256-KqTuc/vUgQsb5EMyyxWf62qYinMUXDpWELyNx+cCUr0 = " crossorigin = "anonymous">
<div>
AAAAA<div id = "permissions"></div>BBB
<script>
function adminUsersPermissionsEditorComponent() {
console.info('adminUsersPermissionsEditorComponent::')
return {
fillUsersPermissions: function () {
var permissionsListing = @json($permissionsListing)
VirtualSelect.init({
ele: '#permissions',
multiple: true,
options: permissionsListing,
});
let selectedPermissions = document.querySelector('#permissions');
selectedPermissions.addEventListener('change', () => {
let data = selectedPermissions.value;
@this.set('selectedPermissions', data);
});
},
onAdminUsersPermissionsEditorComponentInit: function () {
},
}
}
</script>
</div>
</div>
Я вижу доступный элемент множественного выбора, но когда я нажимаю на один из элементов, элемент множественного выбора вообще не отображается.
Что я вижу в консоли браузера:
Как это можно исправить?
"laravel/framework": "^10.48.4",
"livewire/livewire": "^3.4.9",
🤔 А знаете ли вы, что...
JavaScript поддерживает работу с различными форматами данных, такими как JSON и XML.
Основная проблема заключается в том, что каждый раз, когда применяется @this.set('selectedPermissions',....), выполняется вызов бэкэнда, поэтому DOM перерисовывается, а изменения и прослушиватели событий, примененные VirtualSelect, теряются.
Простое решение — добавить провод:игнорировать в <div>, используемый VirtualSelect, таким образом Livewire не будет изменять содержимое этого <div>:
class UsersPermissionsEditor extends Component
{
public $selectedPermissions = [1, 3];
public $permissionsListing = [];
public function render()
{
$this->permissionsListing = Permission::query()
->get(['id', 'name'])
->map(function ($selectedPermissionItem) {
return ['value' => $selectedPermissionItem->id,
'label' => Str::replace('_', ' ', Str::title($selectedPermissionItem->name))
];
})->toArray();
return view('livewire.admin.users-permissions-editor');
}
}
<div class = "admin_page_container" id = "users_permissions_editor_admin_page_container">
<script src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/virtual-select.min.js"
integrity = "sha256-Gsn2XyJGdUeHy0r4gaP1mJy1JkLiIWY6g6hJhV5UrIw = " crossorigin = "anonymous">
</script>
<link rel = "stylesheet"
href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/virtual-select.min.css"
integrity = "sha256-KqTuc/vUgQsb5EMyyxWf62qYinMUXDpWELyNx+cCUr0 = " crossorigin = "anonymous"
>
<div class = "editor_listing_wrapper_bix_width"
x-data = "adminUsersPermissionsEditorComponent('permissions')"
x-cloak
>
<div>
<div wire:ignore id = "permissions"></div>
</div>
</div>
selectedPermissions: {{ implode(', ', $selectedPermissions) }}
</div>
<script>
function adminUsersPermissionsEditorComponent(element) {
return {
init: function () {
VirtualSelect.init({
ele: "#" + element,
multiple: true,
options: @json($permissionsListing),
selectedValue: @json($selectedPermissions)
});
document.getElementById(element).addEventListener("change", function () {
// console.info (this.value);
@this.set("selectedPermissions", this.value);
});
},
}
}
</script>
В этом примере я применил некоторые изменения:
Вот другая версия, созданная мной с использованием Tailwind CSS, Livewire и Alpine, чтобы позволить пользователю выбирать несколько ролей.
<div x-data = "{ open: false, selectedRoles: @entangle('selectedRoles') }" class = "relative">
<x-label for = "roles" required value = "{{ __('Role(s)') }}" />
<div class = "relative mt-2">
<button @click = "open = !open" type = "button" class = "relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6" aria-haspopup = "listbox" aria-expanded = "true" aria-labelledby = "listbox-label">
<span class = "block truncate" x-text = "selectedRoles.length > 0 ? `${selectedRoles.length} role${selectedRoles.length > 1 ? 's' : ''} selected` : '{{ __('Select Roles') }}'">{{ __('Select Roles') }}</span>
<span class = "pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg class = "h-5 w-5 text-gray-400" viewBox = "0 0 20 20" fill = "currentColor" aria-hidden = "true">
<path fill-rule = "evenodd" d = "M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule = "evenodd" />
</svg>
</span>
</button>
<ul x-show = "open" @click.away = "open = false" x-cloak class = "absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" tabindex = "-1">
@foreach($roles as $role)
<li x-bind:class = "selectedRoles.includes('{{ $role->name }}') ? 'bg-gray-50' : ''" @click = "selectedRoles.includes('{{ $role->name }}') ? selectedRoles = selectedRoles.filter(name => name !== '{{ $role->name }}') : selectedRoles.push('{{ $role->name }}')" class = "text-gray-900 relative cursor-default select-none py-2 pl-3 pr-9">
<span x-bind:class = "selectedRoles.includes('{{ $role->name }}') ? 'font-semibold text-sky-800' : 'font-normal'" class = "block truncate">
{{ $role->name }}
</span>
<!-- Display a checkmark if the role is selected -->
<span x-show = "selectedRoles.includes('{{ $role->name }}')" class = "text-sky-600 absolute inset-y-0 right-0 flex items-center pr-4">
<svg class = "h-5 w-5" viewBox = "0 0 20 20" fill = "currentColor" aria-hidden = "true">
<path fill-rule = "evenodd" d = "M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule = "evenodd" />
</svg>
</span>
</li>
@endforeach
</ul>
</div>
</div>
В вашем компоненте Livewire:
public $selectedRoles = [];
public function render()
{
return view('livewire.componenet', [
'roles' => Role::all()
]);
}
Чтобы предварительно заполнить роли, например, если вы редактируете, вы должны загрузить выбранные роли через mount():
public $selectedRoles = [];
public function mount(): void
{
$this->selectedRoles = $this->user->getRoleNames();
}
Выход:
Если вы используете Tall stcak, вам не нужно будет импортировать какие-либо дополнительные библиотеки JS для запуска приведенного выше кода.
В раскрывающемся списке рядом с выбранными элементами и количеством выбранных элементов в поле будет отображаться галочка.