Search code examples
laravellaravel-livewire

Laravel Livewire DOM issues with custom JS


I am new to Laravel Livewire. I am trying to keep a form field where I am using the intlTelInput JS library to add the country code before the phone number.

I have added the JS code in app.js, which is something like this.

import intlTelInput from 'intl-tel-input';
const elementsWithPhoneId = document.querySelectorAll('[id="phone"]');
elementsWithPhoneId.forEach(element => {

    const iti = intlTelInput(element, {
        initialCountry: "bh",
        separateDialCode: true,
        onlyCountries: ['bh', 'sa', 'ae', 'qa', 'kw', 'om', 'us', 'gb'],
        utilsScript: "https://cdn.jsdelivr.net/npm/[email protected]/build/js/utils.js",
    });
});

Fields in the component looks like this

<div class="flex flex-wrap lg:flex-nowrap justify-center w-full">
    <div class="w-full py-2 lg:w-1/2 md:w-1/2 px-2 grid grid-cols-1">
        <label for="inputC1P1" class="w-full font-medium py-1">Primary Phone : <span class="text-red-600"> *</span></label>
        <input
            required
            type="tel"
            class="form-input border border-1 rounded-md w-full my-1 border-gray-300 shadow-md hover:transition-all"
            id="phone"
            placeholder="3344 5566"
            wire:model.blur="C1PrimaryContact"
        />
        <div>
            @error('C1PrimaryContact') <p class="text-red-600 font-light text-xs antialiased italic">{{ $message }}</p>  @enderror
        </div>
    </div>
    <div class="w-full py-2 lg:w-1/2 md:w-1/2 px-2 grid grid-cols-1">
        <label for="inputC1P2" class="w-full font-medium py-1">Secondary Phone :</label>
        <input
            type="tel"
            class="form-input border border-1 rounded-md w-full my-1 border-gray-300 shadow-md hover:transition-all"
            id="phone"
            placeholder="3344 5566"
            wire:model.blur="C1SecondaryContact"
        />
        <div>
            @error('C1SecondaryContact') <p class="text-red-600 font-light text-xs antialiased italic">{{ $message }}</p> @enderror
        </div>
    </div>
</div>

It loads perfectly fine when a component loads for the first time. Once we change something or submit the form, however, it stops working, and the entire app.js stops working.

I have done some alternatives like adding the JS code inside of the component with @script @endscript blade directives or even applied.

document.addEventListener('livewire:init', () => {})

So far, something else is needed. Is there anything I am missing, or do I need to configure something in Livewire settings?


Solution

  • You can avoid the DOM update by Livewire using wire:ignore for example surrounding the inputs with a <div>:

    <div class="w-full py-2 lg:w-1/2 md:w-1/2 px-2 grid grid-cols-1">
    
        <label .... > .... </label>
        
        <div class="...." wire:ignore>
           <input .... />
        <div>
    
        .....
    
    

    Note that you are assigning the same id to both the inputs: this is not a good practice, the id must be unique. You can choose another attribute to select the inputs, for example:

    const elementsWithPhoneId = document.querySelectorAll("input[type=tel]");
    

    a specific class can be another option.

    Also the for attributes of your labels do not match the related input id.

    Edit

    Looking at your repository, I can see that you use redirectRoute() with the navigate: true option. This causes an update of the body without an actual update of the browser document so external libraries must be forced to reinitialize related elements. To achieve this, we can move the code that initializes type-tel inputs into an event-listener for the livewire:navigated event:

    document.addEventListener('livewire:navigated', () => {
    
        const elementsTypePhone = document.querySelectorAll("input[type=tel]");
    
        elementsTypePhone.forEach(element => {
    
            // console.log ("element: ", element);
    
            const iti = intlTelInput(element, {
    
                initialCountry: "bh",
                strictMode: true,
                separateDialCode: true,
                onlyCountries: ['bh', 'sa', 'ae', 'qa', 'kw', 'om', 'us', 'gb'],
                utilsScript: "https://cdn.jsdelivr.net/npm/[email protected]/build/js/utils.js",
            });
        });
    });