laravel-livewirelaravel-11

Nested input fields not showing as expected


Livewire Component

<?php

namespace App\Livewire\Guide;

use App\Models\People;
use Livewire\Attributes\Validate;
use Livewire\Component;

class RequestGuide extends Component
{

    public $nonDasa = false;
    public $guideCopy = false;
    public $people;

    #[Validate('required|min:3')]
    public $person_name = '';

    #[Validate('required|min:3')]
    public $person_email = '';

    #[Validate('required')]
    public $role = '';

    public function mount()
    {
       $this->people = People::all();
    }

    public function render()
    {
        return view('livewire.guide.request-guide')
            ->layout('components.layout');
    }

    public function showNonDasa()
    {
        if($this->nonDasa == false){
            $this->nonDasa = true;
        } else {
            $this->nonDasa = false;
        }
    }

    public function showGuideCopy()
    {
        if($this->guideCopy == false){
            $this->guideCopy = true;
        } else {
            $this->guideCopy = false;
        }
    }

    public function addPerson()
    {
        $this->people->push(new People());
    }

}

request-guide-blade.php

<div class="mx-auto w-7/12">
    {{ session('alert') }}
    <form action="">
        @csrf

        <h1 class="text-3xl font-bold font-roboto-slab text-red-600">Create Guide</h1>
        <x-forms.input label="Guidebook Name" name="name" placeholder="Desired Guidebook Name"/>
        <x-forms.input label="Your Name" name="requestor_name" placeholder="Your Full Name"/>
        <x-forms.input label="Your Email" name="requestor_email" placeholder="unity@ncsu.edu"/>
        <x-forms.checkbox wire:click="showGuideCopy" label="I want to copy content from an existing guide"
                          name="is_copy"/>
        @if($guideCopy)
            <x-forms.input label="Guide to Copy" name="guide_to_copy" placeholder="Copied Guide Name"/>
        @endif
        <x-forms.input label="Your Department" name="requestor_department" placeholder="College, Unit, or Department"/>
        <x-forms.checkbox wire:click="showNonDasa" label="I am requesting a guide for a DASA run event or program"
                          name="is_dasa"/>
        <x-forms.divider/>

        @if ($nonDasa)
            <div id="nonDasaInfo">
                <x-inline-alert>
                    <p>All guides with a start date of 7/1/2024, or after, are billed at $725.</p>
                </x-inline-alert>
                <x-forms.input label="Bookkeeper's Name" name="bookkeeper_name" placeholder="Bookkeeper's Full Name"/>
                <x-forms.input label="Bookkeeper's Email" name="bookkeeper_email" placeholder="unity@ncsu.edu"/>
                <x-forms.input label="Project Code" name="project_code" placeholder="Project Code you'd like billed"/>
            </div>
        @endif

        <div>
            <h2 class="text-xl font-bold font-roboto-slab text-red-600 py-5">Initial Users</h2>
            <p>Your Guide must have at least one Admin user. Admins have all the rights of editors, but can also
                manage the people who can become admins and editors.</p>
            <x-forms.button wire:click="addPerson" class=" bg-red-600 text-white mt-5 mb-2 hover:bg-red-800">
                Add Person
            </x-forms.button>

            <table class="w-full mt-5">
                <thead>
                <tr class="text-center font-bold">
                    <td class="px-2">Person's Name</td>
                    <td>Email</td>
                    <td>Role</td>
                </tr>
                </thead>
                <tbody>
                @foreach ($people as $index => $person)
                    <tr>
                        <td class="px-2">
                            <x-forms.input wire:model="people.{{ $index }}.person_name" label="" name="person_name" placeholder="Full Name" class=""/>
                        </td>
                        <td>
                            <x-forms.input wire:model="people.{{ $index }}.person_email" label="" name="person_email" placeholder="Full Email" class=""/>
                        </td>
                        <td>
                            <x-forms.select wire:model="people.{{ $index }}.role" label="" name="role"
                                            class="bg-gray-600/10 border-gray-600/30 mx-2">
                                <option>Admin</option>
                                <option>Editor</option>
                            </x-forms.select>
                        </td>
                    </tr>
                @endforeach
                </tbody>
            </table>
        </div>
        <div class="text-center pt-10">
            <x-forms.button wire:click="save" class="bg-red-600 text-white mt-5 mb-2 hover:bg-red-800">
                Submit Guide Request
            </x-forms.button>
        </div>
    </form>
</div>

My expectation is that when I click the Add Person button, I'll see the next table row appear with the appropriate index.

When I add this to the mount() function in my Livewire component, I do see a row with the appropriate index, so it seems like the code is working as expected.

$this->people = [['Name','email@email.com','Admin']];

The issue, is that I cannot figure out how to get the array to grow so that it'll add the next (first) row.

Thanks in advance. I'm sure I'm missing something stupid here...


Solution

  • We can't see your forms.button component, however if it doesn't have the type="button" attribute, once the addPerson() method is called, the form is submitted and the new row is lost.

    If you prefer, you can also add the prevent modifier to wire.click and you get the same result. This should work:

    <x-forms.button wire:click.prevent="addPerson" 
                    class="bg-red-600 text-white mt-5 mb-2 hover:bg-red-800"
    >
        Add Person
    </x-forms.button>
    

    A little hint: the showNonDasa() and showGuideCopy() methods can be abbreviated like so:

    public function showNonDasa()
    {
        $this->nonDasa = !$this->nonDasa;
    }