laravellaravel-livewire

Livewire - Multiple root elements detected


I'm creating a table, whose rows are wrapped inside a livewire component

<div class="card">
    <div class="card-body">
        <table class="table table-hover table-striped">
            <thead>
                <tr>
                    <th>Nombre del Riesgo</th>
                    <th>Información</th>
                    <th>Dueño del Riesgo</th>
                    <th>Costo Adicional</th>
                    <th>Prevención</th>
                </tr>
            </thead>
            <tbody>
                <div>
                    @foreach($risks as $risk)
                        <livewire:risk-row :risk="$risk"/>
                    @endforeach
                </div>
            </tbody>
        </table>
    </div>
</div>

But in the row component, whenever I change an input, or select something from a dropdown select, the component is not re rendering.

I was wondering why this could be happening. So I opened my console and saw:

Livewire: Multiple root elements detected. This is not supported. See docs for more information https://laravel-livewire.com/docs/2.x/troubleshooting#root-element-issues <div wire:id=​"6CNMP1hiBR3Qy7IbmfbQ">​ ​</div>​
value @ index.js:85
Component @ index.js:27
(anonymous) @ index.js:88
value @ index.js:87
(anonymous) @ schedule:432

Here's the component blade file

<div>
    <tr>
        <td>{{ $risk['name'] }}</td>
        <td>
            <div><strong>Proceso: </strong>{{ $risk['process']['name'] }}</div>
            <div><strong>Frecuencia: <span class="badge badge-secondary text-sm">{{ $risk['frequency_label'] }}</span></strong></div>
            <div><strong>Impacto: </strong><span class="badge badge-info text-sm">{{ $risk['impact_label'] }}</span></div>
            <div><strong>Riesgo: </strong><span class="badge badge-{{ $risk['risk_color'] }} text-sm">{{ $risk['risk_label'] }}</span></div>
        </td>
        <td>
            <select name="owner" id="owner" class="form-control" wire:change="test">
               @foreach(App\Models\Risk::OWNERS as $owner)
                    <option value="{{ $owner['id'] }}">{{ $owner['name'] }}</option>
               @endforeach
            </select>
            {{ $owner_id }}
        </td>
        <td>
            <select name="owner" id="owner" class="form-control">
                @foreach(App\Models\Risk::COSTS as $cost)
                    <option value="{{ $cost['id'] }}">{{ $cost['name'] }}</option>
                @endforeach
            </select>
        </td>
        <td>
            <select name="owner" id="owner" class="form-control">
                @foreach(App\Models\Risk::PREVENTIONS as $prevention)
                    <option value="{{ $prevention['id'] }}">{{ $prevention['name'] }}</option>
                @endforeach
            </select>
        </td>
    </tr>
</div>

Is there any workaround for this?


Solution

  • When using livewire inside a foreach, you need to add a key.

    <tbody>
        @foreach ($risks as $risk)
            <livewire:risk-row :risk="$risk" :wire:key="$loop->index">
        @endforeach
    </tbody>
    

    Also, you could use the <tr> as the root element and avoid having <div> inside the <tbody>.

    Finally, you're using a lot of id and name attributes that repeat over and over. You should not have duplicate ids. As for the name attributes, you can use the array notation.

    <tr>
        <td>{{ $risk['name'] }}</td>
        <td>
            <div><strong>Proceso: </strong>{{ $risk['process']['name'] }}</div>
            <div><strong>Frecuencia: <span class="badge badge-secondary text-sm">{{ $risk['frequency_label'] }}</span></strong></div>
            <div><strong>Impacto: </strong><span class="badge badge-info text-sm">{{ $risk['impact_label'] }}</span></div>
            <div><strong>Riesgo: </strong><span class="badge badge-{{ $risk['risk_color'] }} text-sm">{{ $risk['risk_label'] }}</span></div>
        </td>
        <td>
            <select name="owner[]" class="form-control" wire:change="test">
               @foreach(App\Models\Risk::OWNERS as $owner)
                    <option value="{{ $owner['id'] }}">{{ $owner['name'] }}</option>
               @endforeach
            </select>
            {{ $owner_id }}
        </td>
        <td>
            <select name="cost[]" class="form-control">
                @foreach(App\Models\Risk::COSTS as $cost)
                    <option value="{{ $cost['id'] }}">{{ $cost['name'] }}</option>
                @endforeach
            </select>
        </td>
        <td>
            <select name="prevention[]" class="form-control">
                @foreach(App\Models\Risk::PREVENTIONS as $prevention)
                    <option value="{{ $prevention['id'] }}">{{ $prevention['name'] }}</option>
                @endforeach
            </select>
        </td>
    </tr>