golveira's avatar

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?

0 likes
0 replies

Please or to participate in this conversation.