What's the best way to create this kind of form in livewire?
Hi,
I want to know if it's possible to create a form like this one using livewire and alpine js. The user can add many cards and I think a draft should be created the first time something is typed in any field.
I've tried some different approaches but I think they all have some drawbacks.
Solution 1: Create a cards array to bind the values with the inputs. It will also use the updated lifecycle hook for creating the draft and updating the data.
public ?Deck $draft= null;
public string $title = "";
public string $description ="";
public array $cards = [];
public function updated($value, $property)
{
// Create the draft
if(!$this->draft) {
$this->draft= Deck::create([...]);
}
// Update the draft
$this->draft->update([...])
// Get the index of the binded card that was updated (ex: cards.0.front or cards.0.back)
// Create a new card model if $this->cards[0]["id"] is null
// Update the associated model if $this->cards[0]["id"] is not null
// Sync $this->cards[0] with the model properties
}
public function addCard()
{
$this->cards[] = ["id" => null, "front" => "", "back" => ""];
}
... other fields
foreach($cards as $card)
{
<div>
<input wire:model.blur="$card.{{ $loop->index }}.front" />
<input wire:model.blur="$card.{{ $loop->index }}.back" />
</div>
}
The problem with this approach is that you have to find the associated model in the $cards array. Also because each field will fire a request on blur, it will make a lot of database queries if a malicious user tries to fill multiple fields at once with an extension like fake filler.
Solution 2: Use of polling + upsert for creating and updating the cards
public function autosave()
{
// Create the draft
if(!$this->draft) {
$this->draft= Deck::create([...]);
}
// Update the draft
$this->draft->update([...])
// Mass update or create
Card::upsert([...])
}
<div wire:poll.10s="autosave">
<form>...</form>
</div>
This solution is simpler than the first one but it will always update the cards even if the data hasn't changed.
Solution 3: Create a separated component for the cards
public ?Deck $draft= null;
public string $title = "";
public string $description ="";
public Collection $cards;
public function updated()
{
.... same logic for creating draft
}
foreach($cards as $card)
{
<livewire:card :$card :key="$loop->index" />
}
The problem with this solution is that it will use a lot of memory if the deck has many cards.
Are any of those solution viable? Can it be made with livewire?
Please or to participate in this conversation.