Sortable not working after switching tabs

Hi all, hoping someone can shed some light on the following:

What seems to be the problem:

I have a livewire components with two HTML tabs. Only the active tab is rendered. On the “photo” tab I have an div containing sortable div’s using https://github.com/livewire/sortable

When the page is loaded with the photo tab open, sorting works. When I switch to the other tab and back, or when I begin on the other tab and switch to the photo tab: sorting doesn’t work. No console errors thrown.

When I put the following hook in the page holding the component:

    Livewire.hook('element.initialized', (element, component) => {
        console.log('initialized ' + element);
    })

And I switch tabs, each time I go to the photo tab I see all the appropiate elements getting initialized.

Steps to Reproduce:

Livewire component:

/**
    * Switch active tab
    */
    public function switchTab($newTab)
    {
        //update the tab
        $this->activeTab = $newTab;

        //define url
        $url = route('object-edit', ['id' => $this->object->id]) . '/' . $newTab;

        //update url to reflect this change
        $this->emit('urlUpdate', $url);
    }
/**

     * Update the order of images

     */

    public function updateImageOrder($images)

    {

        foreach ($images as $image) {

            if (is_int($image['order']) && ctype_digit($image['value'])) {

                $this->object->images->where('id', $image['value'])->first()->update(['rank' => $image['order']]);

            }

        }

        $this->chalet->refresh();

    }

Livewire view:

<div class="hidden lg:block border-b border-gray-200">
      <nav class="-mb-px flex space-x-6" aria-label="Tabs">

        @foreach ($tabs as $tab)
        <button wire:key="tab{{ $loop->index }}" wire:click="switchTab('{{ addslashes($tab) }}')" x-data="{}" x-on:click="$dispatch('switching-tab')" class="border-transparent text-gray-500 hover:text-red-500 hover:border-red-500 whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm">
          {{ $tab }}
        </button>
        @endforeach

      </nav>
    </div>
<div class="w-full min-h-full bg-white rounded-b-lg shadow relative border-t lg:border-none">

      <!-- loading screen -->
      <div wire:loading wire:target="switchTab,activeTab" class="absolute w-full h-full top-0 left-0 bg-white bg-opacity-75 z-10 items-center">

        <div class="flex">

          <svg class="mt-48 animate-spin h-28 w-28 mx-auto text-red-500" viewBox="0 0 24 24" stroke="currentColor" fill="none">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
          </svg>

        </div>

      </div>

      @include('components.editTabs.' . $activeTab)

    </div>
</div>

The active tab for photos component:

<div class="p-8">

    <div wire:sortable="updateImageOrder" class="grid grid-cols-3 lg:grid-cols-4 gap-8">

        @foreach ($object->images as $image)

        <div wire:sortable.item="{{ $image->id }}" wire:key="image-{{ $image->id }}">

            <div class="w-32 h-auto" wire:sortable.handle><img src="{{ asset($image->path) }}"></div>

            {{-- <button wire:click="removeImage({{ $image->id }})">Verwijderen</button> --}}

        </div>

        @endforeach

    </div>

</div>

Are you using the latest version of Livewire:
Yes

Do you have any screenshots or code examples:

Hi, why you have two clicks events in the switching tab button? you can do the action with alpine for faster and smooth tab switching or you can do it with livewire

Yeah. that definetely needs some refactoring, the dispatched event by alpine is received by a “saving status” indicator somewhere else on the page to indicate that we’re saving.

Any thoughts on sortable though? (-:

For anyone else running into similar issues, I resolved by using another sortable library and a very easy solution:

Used: https://github.com/SortableJS/Sortable

The SortableJS library has a store event which runs after the sequence of elements you’re updating has changed. Simply emit the new sort in a livewire event to your livewire component and save the new order from there.

    //initialize sortable
    function initSortable() {

        //the sortable element
        var el = document.getElementById('sortableImages');

        var sortable = Sortable.create(el, {
            store: {
                set: function (sortable) {
                    Livewire.emit('imageSortUpdated', sortable.toArray());
                }
            }
        });
    };