skip to Main Content

I have a modal that I’m using a select2 dropdown menu in. I’ve built a laravel blade component for my select2, and it essentially handles the change event on the select2 and directly sets the variable in livewire using @this.set(myVar, $(select2).val());. That works fine in normal circumstances, where I just want to immediately save the value, but in situations where I want to use a modal with a submit button and use wire:model.defer, it goes ahead and sets the value immediately when the change event is fired, and then when the modal is submitted, it’s just seeing the original value, not the newly selected one.

My first thought is to just override the submitted form value with that of the livewire component variable that’s storing the selected value, and put that in the form instead. I can’t seem to find a good way to do this as I think it’s bad form from a UX perspective, and just doesn’t make much sense in normal scenarios.

The other option would be to just bypass the validation and use the variable value directly, but my concern there is that someone could modify the dom using dev tools and submit whatever value they want.

Has anyone gotten a select2 dropdown working in livewire? I’d love to see a good implementation of this as mine is lacking.

I’m including my rather hackish select2 blade component, currently just returning false when it’s submitted from a modal as I’m working on a workaround.

I want to be able to submit using a submit button in a modal (or somewhere else), basically anytime I’m using wire:model.defer.

Based on the docs, @entangle would be a possible way to do this, but I’m unsure of how to implement it in any other way than with a click event using the sort of show/hide example they give. How could I use it for a change.select2 event if I add this to my select2 blade component declaration:

x-data="{ selectedValue: @entangle($id) }"

It seems like I would need to reference this within my javascript (see below) but it doesn’t work.

@props(['id', 'usePlaceholder' => null, 'isDisabled' => null])

<div wire:ignore style="width:100%;" id="select2-{{ $id }}">
    <select {{ $attributes }} id="{{ $id }}" name="{{ $id }}[]" class="form-select select2" data-width="100%" data-select2-id="{{ $id }}" @if ($isDisabled) disabled @endif>
        @if ($usePlaceholder)
            <option></option>
        @endif
        {{ $slot }}
    </select>
</div>

{{-- blade-formatter-disable --}}
@push('custom-scripts')
<script defer>
    $(document).ready(function() {    
        let select2 = $('#' + @js($id));
        
        select2.select2({
            dropdownParent: $('#select2-' + @js($id)),
            placeholder: 'Select an option',
            minimumResultsForSearch: 15
        });
        

        select2.on('change.select2', function() {

            let classes = document.getElementById(@js($id)).classList;

            if(classes.contains('dynamic-array')) {
                let arr = @js($id).split('-');
                if(arr.length === 3) {
                    let arrayName = arr[0] + '.' + arr[1] + '.' + arr[2];
                    @this.set(arrayName, $(select2).val());
                } else {
                    let arrayName = arr[0] + '.' + arr[1];
                    @this.set(arrayName, $(select2).val());
                }
                
                @this.set(arrayName, $(select2).val());
            } else if(classes.contains('component')){
                let arr = @js($id).split('-');
                let variableName = arr.at(-1);
                @this.set(variableName, $(select2).val());
            } else if (classes.contains('modal')) {
                selectedValue = $(select2).val();  <--not sure how to set this to use with entangle?
                return false;
            }
            else {
                @this.set(@js($id), $(select2).val());
            }
        });

        window.addEventListener('set'+@js(ucfirst($id)), event => {
            select2.val(event.detail).trigger('change');
        });

     });
</script>
@endpush

2

Answers


  1. Chosen as BEST ANSWER

    I ended up just allowing my select2 to set the value on change, and then I'm using the updated lifecycle hook to just validate the value at that point:

    public function updatedClientId($value)
        {
            Validator::make(['clientId' => $value], [
                'clientId' => ['required', 'integer', 'exists:clients,id']
            ])->validate();
        }
    }
    

    then when I submit the form, I don't have that field under validation, and when saving to the database I just use $this->clientId. This seems to work, and displays an error message if someone did try to mess with the dom and submit an invalid value.


  2. You can consider to use 2 below ways:

    1 – Using wire:ignore. A simple example:

    <div wire:ignore style="width:100%;">
        <select wire:model.defer="name">
            <option></option>
            <option value="value1">Name 1</option>
            <option value="value2">Name 2</option>
        </select>
    </div>
    
    <script>
        $("#selectValue").select2();
        $("#selectValue").change(function(e) {
            //passing true as the third parameter to defer
            @this.set('name', $(this).val(), true)
        })
    </script>
    

    2 – Using livewire:update event

    <div style="width:100%;">
        <select wire:model.defer="name">
            <option></option>
            <option value="value1">Name 1</option>
            <option value="value2">Name 2</option>
        </select>
    </div>
    
    <script>
        $("#selectValue").select2();
    
        document.addEventListener('livewire:update', function (event) {
            $("#selectValue").select2();
        });
        
         $("#selectValue").change(function(e) {
           //passing true as the third parameter to defer
            @this.set('name', $(this).val(), true)
         })
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search