Search code examples
laravel-livewirealpine.js

Livewire/Alpine Dynamic # of Toggle Binding


I have a form that displays a number of toggle switches.

enter image description here

The amount of switches is dynamic based on the "permissions" in a table. how do I bind these so that when they are clicked, the data is posted back to livewire? The 'Non-dynamic' answer I have found is x-data="{isChecked: @entangle('foo')}" but this obviously doesn't work if I have an unknown number of items and not a single 'foo'.

I have tried a method wire:click="update({{ $value->id }})" but this only passes back to livewire the id of the element that has been clicked, not it's state (on or off).

@foreach($permissions as $key => $value)
   <div>
      <span>
         <span>{{ $value->name }}</span>
         <span>{{ $value->description }}</span>
      </span>
      <button type="button"
         x-data="{isChecked: {{ $value->allowed ? 1 : 0 }}}"
         @click="isChecked = !isChecked"
         {# wire:click? doesn't send state #}
         wire:click="update"
         :class="{'bg-liteblue': isChecked, 'bg-gray-200': !isChecked }"
         class="relative..."
         role="switch"
         :aria-checked="isChecked"
         aria-labelledby="availability-label">
         <span class="sr-only">Use setting</span>
         <span aria-hidden="true"
            :class="{'translate-x-5': isChecked, 'translate-x-0': !isChecked }"
            class="translate-x-5 transition ease-in-out duration-200"></span>
      </button>
   </div>
@endforeach

I would like each toggle to update the database as its clicked (not in one final submit). How do I pass the state back to the livewire controller?


Solution

  • I solved the issue with a child component called 'permission-toggle', that way each toggle could be responsible for holding it's state rather than an array, and then instantiated it inside the parent component with

    @foreach($permissions as $permission)
    
            <livewire:permission-toggle :group="$selectedGroup" :permission="$permission" :key="$selectedGroup.'.'.$permission->slug">
    
    @endforeach
    

    The parent "permission-form" component is responsible for getting all the permissions from the database, (so there's only one big call on load) and then each individual "permission-toggle" component is responsible for updating itself. (individual database calls) The livewire controller for permission toggle is as below

    class PermissionToggle extends Component
    {
        public bool   $checked;
        public string $name;
        public string $description;
        public string $slug;
        public string $group;
    
        public function mount($permission)
        {
            $this->checked     = $permission->allowed;
            $this->name        = $permission->name;
            $this->description = $permission->description;
            $this->slug        = $permission->slug;
        }
    
        public function updating($a, bool $checked)
        {
            $group = Group::where('slug', $this->group)->first();
    
            if($checked === true)
                $group->assignPermissions($this->slug);
            else
                $group->revokePermissions($this->slug);
        }
    
        public function render()
        {
            return view('livewire.permission-toggle');
        }
    }