javascriptphplaravellaravel-livewiremulti-select

Dropdown menu disappears after form submission in livewire component


I'm working on a Laravel Livewire component with a multi-select dropdown. The dropdown options are dynamically generated with JavaScript, but after submitting the form, the dropdown options disappear. Here’s the HTML for the dropdown:

<div id="dropdown" style="display: none;"
    class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
    wire:ignore.self>
    <!-- Dynamically generated options will go here -->
</div>

The problem is that I can’t use the latest version of Alpine.js with @livewireScripts because I’m using a library that includes Alpine v2.8. Unfortunately, I can’t extract the required Alpine.js code from the library since it’s all mixed together.

Everything works fine until the form is submitted and the Livewire component refreshes, at which point the dropdown disappears.

This is the frontend code:

<div class="relative">
    <label class="block text-sm mb-1" for="roles">Mention roles</label>
    <div class="mt-1 relative">
        <button type="button" id="toggleDropdown"
            class="relative w-full bg-white border border-gray-200 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-primary focus:border-primary sm:text-sm">
            <span id="displayText" class="block truncate">Select roles</span>
            <span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
                <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
                    <path fill-rule="evenodd"
                        d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
                        clip-rule="evenodd" />
                </svg>
            </span>
        </button>

        <div id="dropdown" style="display: none;"
            class="absolute z-10 mt-1 w-full bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
            wire:ignore.self>
            <!-- Dynamically generated options will go here -->
        </div>
    </div>
</div>

<input type="hidden" id="selectedRoles" wire:model="selectedRoles">

<script>
                    let isDropdownInitialized = false; // Flag to track if dropdown is initialized

                    function initializeDropdown(roles) {
                        console.log('Initializing dropdown with roles:', roles); // Debug: Check roles passed to the function

                        const selectedRolesInput = document.getElementById('selectedRoles');
                        const toggleDropdownButton = document.getElementById('toggleDropdown');
                        const dropdown = document.getElementById('dropdown');
                        const displayText = document.getElementById('displayText');
                        const dropdownContainer = document.querySelector('.relative');

                        let selectedOptions = [];

                        // Function to create dropdown items
                        function createDropdownItems() {
                            console.log('Creating dropdown items'); // Debug: Track when dropdown items are created
                            dropdown.innerHTML = ''; // Clear previous dropdown items

                            roles.forEach(role => {
                                console.log('Creating item for role:', role); // Debug: Track each role being added

                                const div = document.createElement('div');
                                div.classList.add('cursor-pointer', 'select-none', 'relative', 'py-2', 'pl-3', 'pr-9',
                                    'hover:bg-primary', 'hover:text-white');
                                div.setAttribute('data-role-id', role.id);
                                div.addEventListener('click', function(event) {
                                    console.log('Role clicked:', role); // Debug: Track role selection
                                    event.stopPropagation();
                                    toggleOption(role);
                                });

                                const span = document.createElement('span');
                                span.textContent = role.name;
                                span.classList.add('block', 'truncate');

                                const checkIcon = document.createElement('span');
                                checkIcon.classList.add('absolute', 'inset-y-0', 'right-0', 'flex', 'items-center', 'pr-4',
                                    'text-primary');
                                checkIcon.style.display = selectedOptions.some(selected => selected.id === role.id) ? 'block' :
                                    'none';

                                const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
                                svg.setAttribute('class', 'h-5 w-5');
                                svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
                                svg.setAttribute('viewBox', '0 0 20 20');
                                svg.setAttribute('fill', 'currentColor');

                                const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                                path.setAttribute('fill-rule', 'evenodd');
                                path.setAttribute('d',
                                    'M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z'
                                    );
                                path.setAttribute('clip-rule', 'evenodd');

                                svg.appendChild(path);
                                checkIcon.appendChild(svg);
                                div.appendChild(span);
                                div.appendChild(checkIcon);
                                dropdown.appendChild(div);
                            });
                        }

                        // Toggle the dropdown visibility
                        toggleDropdownButton.addEventListener('click', function(event) {
                            console.log('Dropdown toggle clicked'); // Debug: Track toggle button click
                            event.stopPropagation();
                            dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
                        });

                        // Toggle selection of a role
                        function toggleOption(role) {
                            console.log('Toggling selection for role:', role); // Debug: Track role selection toggle
                            const index = selectedOptions.findIndex(selected => selected.id === role.id);
                            if (index !== -1) {
                                selectedOptions.splice(index, 1);
                            } else {
                                selectedOptions.push(role);
                            }
                            updateDisplayText();
                            createDropdownItems();
                            updateSelectedRolesInput();
                        }

                        // Update the displayed text of selected roles
                        function updateDisplayText() {
                            console.log('Updating display text with selected options:',
                            selectedOptions); // Debug: Track selected options
                            displayText.textContent = selectedOptions.length ? selectedOptions.map(option => option.name)
                                .join(', ') : 'Select roles';
                        }

                        // Update the hidden input field with selected roles
                        function updateSelectedRolesInput() {
                            const selectedIds = selectedOptions.map(option => option.id).join(',');
                            selectedRolesInput.value = selectedIds;
                            selectedRolesInput.dispatchEvent(new Event('input'));
                            console.log('Updated selected roles input:', selectedIds); // Debug: Track updated input value
                        }

                        // Initialize the dropdown items
                        createDropdownItems();

                        // Close the dropdown if clicked outside
                        document.addEventListener('click', function(event) {
                            if (!dropdownContainer.contains(event.target) && dropdown.style.display === 'block') {
                                console.log('Closing dropdown (clicked outside)'); // Debug: Track dropdown close action
                                dropdown.style.display = 'none';
                            }
                        });
                    }

                    document.addEventListener('livewire:initialized', () => {
                        @this.on('send-message', (data) => {
                            console.log('Message sent!'); // Debug: Track when message is sent
                            console.log('Roles:', data.roles); // Debug: Check roles passed
                            roles = data.roles; // Assign the roles to the roles variable
                            initializeDropdown(roles); // Reinitialize the dropdown with the new roles
                        });
                    });

                    document.addEventListener('DOMContentLoaded', () => {
                        console.log('Document loaded'); // Debug: Track when the page is loaded
                        // Initialize dropdown when the page is loaded
                        initializeDropdown(
                        @json($roles)); // Assuming @json($roles) is available in the page
                    });
                </script>

And this is the back-end code:

<?php

namespace App\Livewire;

use Livewire\Component;
use Illuminate\Support\Facades\Log;

class ManageMessage extends Component
{
    public $server;
    public $channels;
    public $channel;
    public $message;
    public $roles = [];
    public $selectedRoles;
    public $webhook = null;

    public function mount($server, $roles, $channels)
    {
        $this->server = $server;
        $this->roles = $roles;
        $this->channels = $channels;
    }

    public function send()
    { 
        // actions unrelated to the problem...

        try {
            $this->dispatch('send-message', roles: $this->roles);
        } catch (\Exception $e) {
            Log::error('Error sending message: ' . $e->getMessage());
            flash()->flash('error', 'An error occurred while sending the message.', [], 'Error');
            $this->dispatch('send-message', roles: $this->roles);
        }
    }
}

This is where the Livewire component is called:

@livewire('manage-message', ['server' => $server, 'roles' => $roles, 'channels' => $channels ?? []])

The dropdown options disappear after form submission.

I have added console.log in the code to debug and this is the output:

Before form submission:

livewire.js?id=def850b5:10067 Detected multiple instances of Alpine running
warnAboutMultipleInstancesOf @ livewire.js?id=def850b5:10067
(anonymous) @ livewire.js?id=def850b5:10071
(anonymous) @ livewire.js?id=def850b5:10083Understand this warningAI
1107966087917731870:419 Document loaded
1107966087917731870:301 Initializing dropdown with roles: (11) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
1107966087917731870:313 Creating dropdown items
1107966087917731870:317 Creating item for role: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:317 Creating item for role: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:317 Creating item for role: {id: '1147154575846019094', name: 'Test Application', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1147539667013619795', name: 'Sentinel', description: null, permissions: 2145385983, permissions_new: '70368742079999', …}
1107966087917731870:317 Creating item for role: {id: '1148648927227297873', name: 'COOL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1149757414372024471', name: 'KEWL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1163927957954371745', name: 'Punishment', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1167503790170181733', name: 'test ping', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1184202431102468096', name: 'TEST', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1246784542644371544', name: 'Embedinator', description: null, permissions: 537259016, permissions_new: '537259016', …}
1107966087917731870:317 Creating item for role: {id: '1311297473003913219', name: 'Sentinel Debugging', description: null, permissions: 2146959359, permissions_new: '2108859006582783', …}
1107966087917731870:1 Unchecked runtime.lastError: The message port closed before a response was received.Understand this errorAI
1107966087917731870:1 Unchecked runtime.lastError: The message port closed before a response was received.Understand this errorAI
1107966087917731870:362 Dropdown toggle clicked
 Role clicked: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:369 Toggling selection for role: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:383 Updating display text with selected options: [{…}]
1107966087917731870:313 Creating dropdown items
1107966087917731870:317 Creating item for role: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:317 Creating item for role: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:317 Creating item for role: {id: '1147154575846019094', name: 'Test Application', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1147539667013619795', name: 'Sentinel', description: null, permissions: 2145385983, permissions_new: '70368742079999', …}
1107966087917731870:317 Creating item for role: {id: '1148648927227297873', name: 'COOL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1149757414372024471', name: 'KEWL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1163927957954371745', name: 'Punishment', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1167503790170181733', name: 'test ping', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1184202431102468096', name: 'TEST', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1246784542644371544', name: 'Embedinator', description: null, permissions: 537259016, permissions_new: '537259016', …}
1107966087917731870:317 Creating item for role: {id: '1311297473003913219', name: 'Sentinel Debugging', description: null, permissions: 2146959359, permissions_new: '2108859006582783', …}
1107966087917731870:394 Updated selected roles input: 1107966087917731870
1107966087917731870:324 Role clicked: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:369 Toggling selection for role: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:383 Updating display text with selected options: (2) [{…}, {…}]
1107966087917731870:313 Creating dropdown items
1107966087917731870:317 Creating item for role: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:317 Creating item for role: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:317 Creating item for role: {id: '1147154575846019094', name: 'Test Application', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1147539667013619795', name: 'Sentinel', description: null, permissions: 2145385983, permissions_new: '70368742079999', …}
1107966087917731870:317 Creating item for role: {id: '1148648927227297873', name: 'COOL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1149757414372024471', name: 'KEWL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1163927957954371745', name: 'Punishment', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1167503790170181733', name: 'test ping', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1184202431102468096', name: 'TEST', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}color: 0description: nullflags: 0hoist: falseicon: nullid: "1184202431102468096"managed: falsementionable: falsename: "TEST"permissions: 2146959359permissions_new: "420009146318847"position: 6unicode_emoji: null[[Prototype]]: Object
1107966087917731870:317 Creating item for role: {id: '1246784542644371544', name: 'Embedinator', description: null, permissions: 537259016, permissions_new: '537259016', …}
1107966087917731870:317 Creating item for role: {id: '1311297473003913219', name: 'Sentinel Debugging', description: null, permissions: 2146959359, permissions_new: '2108859006582783', …}
1107966087917731870:394 Updated selected roles input: 1107966087917731870,1145087335164297309
1107966087917731870:403 Closing dropdown (clicked outside)
1107966087917731870:411 Message sent!
1107966087917731870:412 Roles: (11) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
1107966087917731870:301 Initializing dropdown with roles: (11) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
1107966087917731870:313 Creating dropdown items
1107966087917731870:317 Creating item for role: {id: '1107966087917731870', name: '@everyone', description: null, permissions: 238407233, permissions_new: '2222085320855105', …}
1107966087917731870:317 Creating item for role: {id: '1145087335164297309', name: 'Admin', description: null, permissions: 8, permissions_new: '8', …}
1107966087917731870:317 Creating item for role: {id: '1147154575846019094', name: 'Test Application', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1147539667013619795', name: 'Sentinel', description: null, permissions: 2145385983, permissions_new: '70368742079999', …}
1107966087917731870:317 Creating item for role: {id: '1148648927227297873', name: 'COOL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1149757414372024471', name: 'KEWL', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1163927957954371745', name: 'Punishment', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1167503790170181733', name: 'test ping', description: null, permissions: 0, permissions_new: '0', …}
1107966087917731870:317 Creating item for role: {id: '1184202431102468096', name: 'TEST', description: null, permissions: 2146959359, permissions_new: '420009146318847', …}
1107966087917731870:317 Creating item for role: {id: '1246784542644371544', name: 'Embedinator', description: null, permissions: 537259016, permissions_new: '537259016', …}
1107966087917731870:317 Creating item for role: {id: '1311297473003913219', name: 'Sentinel Debugging', description: null, permissions: 2146959359, permissions_new: '2108859006582783', …}

After form submission:

181107966087917731870:362 Dropdown toggle clicked

Solution

  • For the solution, I wrapped my select with this div:

    <div wire:ignore>
     <!-- content in here -->
    </div>
    

    And that fixed the issue.