dom-eventslaravel-bladelaravel-livewirealpine.js

Stop propagation or other event needed for clicking in checkbox label?


My blade component show-on-check seems to have an error with alpine.js. It uses a custom checkbox-input with a label. Input and label are clickable.

Component show-on-check:

@props([
    'label',
    'name',
])

@php
    $show = old('show_content_'.$name);
@endphp

<div x-data="{ show: {{ $show ? 'true' : 'false' }} }" class="mb-3">
    <x-input.checkbox name="show_content_{{$name}}" x-model="show" :label="$label"/>
    <div x-show="show" class="border border-gray-300 p-3">
        {{ $slot }}
    </div>
</div>

show-on-check is then nested inside a livewire component show-dns-entries.

Component show-dns-entries:

<div>
    @if (count($childrenIps))
        <x-show-on-check label="rDNS-Einträge anzeigen" name="rdns">
            <ul>
                @foreach($childrenIps as $childIp)
                    <li>
                        {{ $childIp->dns_name }} ({{ $childIp->address }})
                    </li>
                @endforeach
            </ul>
        </x-show-on-check>
    @else
        <x-typo.muted-xs>Keine DNS-Einträge</x-typo.muted-xs>
    @endif
</div>

show-dns-entries is nested inside a table like so:

<x-table.body>
    @foreach($paginatedIps as $ip)
        <x-table.row>
            //
            <x-table.col>
                //
                <livewire:maintenance::show-dns-entries-for-prefix :prefix="$ip->prefix"/>
                //
            </x-table.col>
          //
        </x-table.row>
    @endforeach

The show functionality of alpine works fine if I click the check-box-input directly, but if I click the label of show-on-check instead of the checkbox-input, it always triggers show for the first show-on-check and not the one I actually clicked.

This is the code of the <x-input.checkbox> component I use inside the show-on-check component:

@props([
    'name',
    'label' => null,
    'checked' => false,
    'class' => '',
    'wrapperClass' => 'mb-3'
])
@php($i18nName = I18n::arrayToDotNotation($name ?? ''))

<div class="{{ $wrapperClass }}">
    <input type="checkbox" {{ $attributes }} id="{{ $name }}" name="{{ $name }}" @checked(old($i18nName) ?? $checked) class="{{$class}} rounded border-gray-400 text-primary shadow-sm focus:ring-0">
    @if($label)
        <label for="{{ $name }}" class="text-s text-gray-600">{{ $label }}</label>
    @endif
    @if($errors->has($i18nName))
        <div class="text-red-500 text-xs">{{ $errors->first($i18nName) }}</div>
    @endif
</div>

I tried to add @click.stop to the label, but that did nothing. I can use @click.prevent of course, but I would prefer the label to remain clickable.

Please let me know if you need more info.


Solution

  • From the posted code it seems that the show-on-check receives always the same name (rdns) so all the checkboxes have the same id and all the labels point to the first checkbox with that id.

    You could work around the problem removing the id from the checkboxes and changing the input.checkbox component like so:

    @props([.....
    
    .....
    
    <div class="{{ $wrapperClass }}">
    
        <label>
    
            <input type="checkbox"
                   {{ $attributes }}
                   name="{{ $name }}"
                   @checked(old($i18nName) ?? $checked)
                   class="{{$class}} rounded border-gray-400 text-primary shadow-sm focus:ring-0"
            >
    
            @if($label)
                <span class="text-s text-gray-600">
                    {{ $label }}
                </span>
            @endif
    
        </label>
    
    .....
    

    Also, the snippet of the table you posted seems to have some problems: the Livewire component included in the @foreach loop seems to have a malformed name, also if it is a nested Livewire component you may need to add a unique :key attribute to it (Here the details)