Wire:ignore With Google Autocomplete

I am pulling my hair:

I pulled npm google-address-autocomplete and initialized laravel component google-address-lookup:

<input
    x-data="{address:{}}"
    x-init="new AddressAutocomplete('.google-address-lookup', function (result, raw) {
        address = result;
        $dispatch('input', address);
    });"
    wire:ignore
    type="text"
    class="form-control google-address-lookup"
    required
    autocomplete="off"
    {{ $attributes }}
>

Livewire component is

<x:google-address-lookup wire:model="address"/>

Yet as I enter the partial address, the component makes a run to the server (i do not expect this); and now i have new input with a partial value injected without an event listener, which makes it useless, unless I manage to enter the value within a second.

I must be missing something super obvious

I did this but decided to use the WebServices API instead.

Route::get('autocomplete/{input}', function ($input) {
    return Http::get('https://maps.googleapis.com/maps/api/place/autocomplete/json', [
        'input' => $input,
        'types' => 'address',
        'sessiontoken' => session()->getId(),
        'key' => env('GOOGLE_MAPS_API_KEY')
    ]);
});
<div class="..." wire:ignore.self
     wire:model.lazy.debounce.500ms="a.{{$input->id}}"
     x-on:click.away="dropdown = false"
     x-data="{
        dropdown: false,
        results: [],
        autocomplete(input) {
            this.dropdown = true;
            if(input) {
                axios.get('/api/autocomplete/'+input).then(results => {
                    this.results = results.data.predictions
                })
            } else { this.results = []; }
        }
    }"
>
    <label class="...">{{ $input->objectable->answer }}</label>
    <div class="...">
        <input type="text" name="{{$input->id}}"
               x-on:input.debounce.stop="autocomplete($event.target.value)"
               x-on:keydown.enter.prevent="$refs.address.value = results[0].description; @this.set('a.place_id', results[0].place_id); dropdown = false"
               x-ref="address" value="{{$this->a[$input->id] ?? null}}"
               class="@error('a.'.$input->id) error @enderror"
        />
        <div wire:loading.class.remove="hidden" wire:target="a.{{$input->id}}" class="...">
            <i class="fad fa-spinner-third fa-fw fa-spin text-gray-400"></i>
        </div>
        <div wire:ignore>
            <template x-if="dropdown && results.length > 0">
                <div class="dropdown">
                    <div class="rounded-md bg-white shadow-xs">
                        <div class="py-1">
                            <template x-for="result in results">
                                <a x-on:click="$refs.address.value = result.description; @this.set('a.place_id', result.place_id); dropdown = false" x-text="result.description"
                                   href="javascript:void(0)" class="..."></a>
                            </template>
                        </div>
                    </div>
                </div>
            </template>
        </div>
    </div>

    @error('a.'.$input->id) <span class="...">{{ $message }}</span> @enderror
</div>

Http::get('https://maps.googleapis.com/maps/api/place/details/json', [
    'place_id' => $answer['answers'][$ans->objectable->crm_value]['place_id'],
    'fields' => 'address_component,geometry',
    'sessiontoken' => session()->getId(),
    'key' => env('GOOGLE_MAPS_API_KEY')
])->json()['result'];

I have solved it with two intermediaries:

Livewire component includes a blade component:

<form wire:submit.prevent="createAddress">
    <div class="form-group" >
        <x:google-address-lookup wire:model.lazy="lookup" />
    </div>
    <div class="form-group">
        <button
            type="submit"
            class="btn btn-outline-secondary"
            wire:submit.prevent="createAddress">{{__('Add Address')}}</button>
    </div>
</form>

A Blade component

<div class="input-group"
    wire:ignore
    x-data={lookup:{}}
    x-init="() => {
        new AddressAutocomplete('.google-address-lookup', (result, raw) => {
            @this.set('lookup', result)
        });
    }"
    {{ $attributes }}
    >
    <input
        x-data
        x-on:address:list:refresh.window="$('.google-address-lookup').val('')"
        x-on:keydown.enter.prevent
        wire:ignore.self
        wire:loading.attr="readonly"
        autocomplete="google-lookup"
        class="form-control google-address-lookup"
        required
        type="search"
    >
</div>

So that the livewire model “lookup” is wrapping the input manipulated by Google PAC
This way the input value is a string and out $address model is an array, as needed.

I expect that the user may want to enter multiple addresses, one after the other, so i clear the input (hence the events)

Now, in Livewire component class, cleaned up for brevity

namespace App\Http\Livewire\Customer\Address;

use App\Actions\Customer\CreateGoogleAddress;
use App\Codices\Customer\NewGoogleAddressCodex;
use App\Models\Customer;
use Livewire\Component;

class Create extends Component
{
    /**
     * Bind the Model
     *
     * @var App\Models\Customer
     */
    public $customer;

    /**
     * Lookup intermediary
     *
     * @var
     */
    public $lookup;

    /**
     * Address
     *
     * @var array
     */
    public $address = [];

    /**
     * Mount component
     *
     * @param  App\Models\Customer $customer
     * @return void
     */
    public function mount(Customer $customer)
    {
        $this->customer = $customer;
    }

    /**
     * Render the component
     *
     * @return Illuminate\View\View
     */
    public function render()
    {
        return view('livewire.customer.address.create');
    }

    /**
     * Listen to updating event
     * Push the value of lookup into the $address
     *
     * @param  string $field
     * @param  mixed $value
     * @return void
     */
    public function updating($field, $value)
    {
        if ($field == 'lookup' and is_array($value)) {
            $this->address = $value;
        }
    }

    /**
     * Listen to updated event
     *
     * @param  string $field
     * @return array
     */
    public function updated($field)
    {
        $this->validateOnly($field, [
            'address.cityName' => 'required|string',
            'address.country' => 'required|string',
            'address.state' => 'required|string',
            'address.streetName' => 'required|string',
            'address.streetNumber' => 'nullable|string',
            'address.zipCode' => 'required|string',
        ]);
    }

    /**
     * Create customer address
     *
     * @return
     */
    public function createAddress()
    {
        $this->customer->addresses()->create($this->address);

        $this->dispatchBrowserEvent('address:list:refresh');
    }
}

I do not get the feeling that it is overly hacky; what do you think, @calebporzio ?

1 Like

hi
need some help

if i do somthing like that:

@if($show)
<x:google-address-lookup wire:model=“address”/>
@endif

the auto autocomplete didnt work
do u have how to fix that??