Skip to content

Instantly share code, notes, and snippets.

@tanthammar
Last active August 9, 2022 20:52
Show Gist options
  • Save tanthammar/3374780f961ef53508e39cec4cdd7227 to your computer and use it in GitHub Desktop.
Save tanthammar/3374780f961ef53508e39cec4cdd7227 to your computer and use it in GitHub Desktop.
Livewire Sortable fix with array form
<?php
namespace App\Http\Livewire\Traits;
trait Form
{
public function reorder($reordered, $existing)
{
if (count($reordered) > 0) {
foreach ($reordered as $order) {
$new_order[] = $existing[$order['value']];
}
return $new_order;
} else {
return $existing;
}
}
}
// solves the issue to have multiple draggable instances on the same page
// until caleb merges https://github.com/livewire/sortable/pull/7
import Sortable from '@shopify/draggable/lib/sortable';
window.livewire.directive('sortable-alt', (el, directive, component) => {
// Only fire this handler on the "root" directive.
if (directive.modifiers.length > 0) return
let options = { draggable: '[wire\\:sortable\\.item]' }
if (el.querySelector('[wire\\:sortable\\.handle]')) {
options.handle = '[wire\\:sortable\\.handle]'
}
const sortable = new Sortable(el, options);
sortable.on('sortable:stop', () => {
setTimeout(() => {
let items = []
el.querySelectorAll('[wire\\:sortable\\.item]').forEach((el, index) => {
items.push({ order: index + 1, value: el.getAttribute('wire:sortable.item') })
})
component.call(directive.method, items)
}, 1)
})
})
@push('scripts')
<script src="{{ mix('js/livewire-custom-sortable.js') }}"></script>
@endpush
<form>
<div wire:sortable-alt="updatePeopleOrder" id="update-people-order">
@foreach($people as $i => $person)
<div wire:sortable.item="{{ $loop->index }}" wire:key="person-{{ $loop->index }}">
<div>
<button @click.prevent.stop wire:sortable.handle>
your dragme button
</button>
<button wire:click.prevent="deleteItem({{ $i }})">
delete item button
</button>
</div>
{{-- click.stop is needed to be able to select text (click+hold+drag) in form inputs, otherwise it activates the dragging behaviour --}}
<div @click.stop @touchstart.stop @mousedown.stop>
<input
name="title{{ $i }}"
type="text"
wire:model.lazy="people.{{ $i }}.title"
class="form-input @error("people.{$i}.title") error placeholder-red-300 @enderror" />
<input
name="first_name{{ $i }}"
type="text"
wire:model.lazy="people.{{ $i }}.first_name"
class="form-input @error("people.{$i}.first_name") error placeholder-red-300 @enderror" />
{{-- and more input tags --}}
</div>
</div>
@endforeach
</div>
{{-- add item --}}
<button wire:click.stop="addItem">Add Item</button>
{{-- submit button --}}
</form>
<?php
namespace App\Http\Livewire\App\Organizers\Forms;
use App\Http\Livewire\Traits\Form;
use App\Models\Organizer;
use Livewire\Component;
class People extends Component
{
use Form;
public $model;
public $people;
public function mount(Organizer $organizer)
{
$this->fill([
'model' => $organizer,
'people' => $organizer->people,
]);
}
public function updatePeopleOrder($reordered)
{
$this->people = $this->reorder($reordered, $this->people);
}
public function deleteItem(int $i): void
{
unset($this->people[$i]);
$this->people = array_values($this->people);
}
// example validation rules
public function getRules()
{
return [
'people.*.title' => 'required|between:2,50',
'people.*.first_name' => 'required|between:2,50',
];
}
public function addItem(): void
{
$this->people[] = [
'title' => '',
'first_name' => '',
];
}
public function save()
{
$this->validate($this->getRules());
$this->model->update([
'people' => $this->people,
]);
$this->notify(); //see calebs sponsor screencasts
}
}
@tanthammar
Copy link
Author

tanthammar commented May 15, 2020

This is an example of how to create a livewire form that saves array data to a json db field.
It also solves the issue with livewire/sortable, having multiple instances on the same page.

Methods:

  • reorder items (drag)
  • validate array items
  • add empty item
  • delete item

@tanthammar
Copy link
Author

livewireDraggable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment