Custom multi-select component inconsistent on modal

I created a custom multiple select blade component and implemented alpinejs. It works perfectly fine on a full-page livewire component. When I include it on a modal that is available to each page and is included in the layouts page, I lose some reactivity with the custom multiple select.

The Livewire component has a public property $selected that is set by default to []. When a “editCustomer” event is raised, the component listener populates the value and then opens the modal.

Here’s what I have for the blade component:

@props(['options' => []])
<div x-data="tags({selected: @entangle($attributes->wire('model')), options: {{ $options }}})" class="relative bg-white overflow-y-visible z-10" @click.away="open = false">
  <div class="border-2 border-gray-200 flex flex-wrap p-1 rounded-md text-sm">
    <template x-for="(item, index) in getSelected()" :key="index">
      <div class="flex border-2 rounded-full py-1 px-2 m-1 bg-gray-100">
        <div class="flex items-center">
          <span x-text="item[labelField]"></span>
          <button class="focus:outline-none" x-on:click="removeItem(item[valueField])">
            <svg class="h-4 w-4 ml-1 cursor-pointer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                 stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
            </svg>
          </button>
        </div>
      </div>
    </template>
    <input x-on:click="toggleOpen()" class="ml-2 outline-none h-10" x-model="tag" type="text"/>
  </div>
  <ul x-show="open" class="absolute w-full overflow-auto bg-white mt-1 p-1 border-2 border-gray-200 rounded-md space-y-1">
    <template x-for="option in options" :key="option[valueField]">
      <li class="px-2 py-1 hover:bg-indigo-500 hover:text-white cursor-pointer rounded-md"
          x-bind:class="isSelected(option[valueField]) ? 'bg-indigo-200' : ''"
          x-on:click="toggleSelected(option[valueField])"
      >
        <span x-text="option[labelField]"></span>
      </li>
    </template>
  </ul>
</div>

@push('scripts')
  <script>
      function tags(config) {
          return {
              open: false,
              tag: '',
              selected: config.selected ?? [],
              options: config.options ?? [],
              valueField: 'id',
              labelField: 'name',
              toggleOpen() {
                  this.open = ! this.open;
              },
              getSelected()
              {
                  return this.options.filter(option => {
                      return this.selected.includes(option[this.valueField]);
                  });
              },
              isSelected(value) {
                  return this.selected.includes(value);
              },
              toggleSelected(value) {
                  if (this.selected.includes(value)) {
                      this.selected = this.selected.filter(sel => {
                          return sel !== value;
                      })
                  } else {
                      this.selected = this.selected.concat([value]);
                  }
              },
              removeItem(value) {
                  this.selected = this.selected.filter(sel => {
                      return sel !== value;
                  })
              }

          }
      }
  </script>
@endpush

The implementation is simple:

<x-input.multiple-select wire:model.defer="selected" :options="$contacts" />

I have tried removing the “defer” modifier and it does improve but requires 2 clicks of the button rather than just one.

My issue is that when I use in a modal, the selected values are present but I am unable to remove an existing option from the UI by clicking the button to removeItem, although wire:model is updated. However, clicking the <li> removes the items from the UI as expected.

Any item I add from the UI can be removed by clicking the removeItem button but values set from the database cannot. This seems to be a reactivity issue where alpine isn’t aware of the items.

Here’s a short video: https://magnigen-public.s3.amazonaws.com/livewire_alpinejs_reactivity_issue.mp4

Any help is appreciated.

I did the simple thing and looped over the options and showed ones that were selected:

<template x-for="option in options" :key="option[valueField]">
      <div x-show="isSelected(option[valueField])" class="flex border-2 rounded-full py-1 px-2 m-1 bg-gray-100">
        <div class="flex items-center">
          <span x-text="option[labelField]"></span>
          <button class="focus:outline-none" x-on:click="toggleSelected(option[valueField])">
            <svg class="h-4 w-4 ml-1 cursor-pointer" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                 stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"/>
            </svg>
          </button>
        </div>
      </div>
    </template>