Data binding not working with value changed by JS

I will try to describe as best as possible:

I have a simple summernote (wysiwyg editor) instance on a page that writes the data to an input type hidden. This is a model of description wire:model="description"

This doesn’t work.

If I change the input to text it also doesn’t work

<input type="text" wire:model="description" id="description" />

However, if I manually type into the text input the binding works fine.
It seems Livewire only reads data if the user interacts manually.

Any solution for this?

1 Like

wire:model is updated when the input event is updated. You can either call the update of your input to trigger the update or you can call a livewire event and pipe the new update into your backend. The simplest approach might be to fire the input of the description input after setting the value.

After you set the value, you can do something like:

var element = document.getElementById('description');
element.dispatchEvent(new Event('input'));

This should trigger the input event on the field and cause livewire to communicate to your backend.

5 Likes

Thanks, that solves the issue however, it feels hackish

It’s just how livewire triggers communication. If you want something more methodical, you can try to pass the value directly with a livewire event, thus bypassing the input field altogether. The reason behind only sending requests upon input event is to cut down on traffic to your server. You wouldn’t necessarily want a request generated for every single keystroke.

I get that but using .lazy solves that issue.

.lazy affects the behavior of the field altogether - it’s triggered by the blur() of the field rather than keyboard input. The input event is triggered by monitoring of keyboard input on the field and by default has a debounce to limit amount of requests. Since you’re programmatically setting the field, there’s no actual keyboard input to trigger the event. That’s why you have to fire that event manually. There are other solutions that might make it feel less hacky but triggering the input event is fine and works well.

I agree, that’s what I’m gonna use but it would be nice to have a livewire specific solution. Something like wire:model.something that would make it listen

You could try to attach wire:poll but that’s not as direct of an approach as sending the data when it’s ready to be sent. You’re already attaching it to a variable, just go ahead and send it to the backend when it’s ready.

Hi @shortbrownman

I have an issue on my project.
I want to build a searhbar using livewire.
But it calls search event for every keyup event.
I want to limit API call at 1 call per 1~2 sec.
Any suggestion?

(NOTE: event debounce not working at all. wire:keydown.debounce.2000ms =" doSearch" doesn’t work
Thanks

The proper syntax for debouncing should be <input type="text" wire:model.debounce.2000ms="doSearch">

Just to be sure: doSearch is a variable, not a method.

Thanks @shortbrownman
I know that.
But I want to put limit for keydown event
Even the model debounce doesn’t work correctly

I’m unsure why you’d hook into your wire:model this way. The usual way would be to let the model update in the backend and have your doSearch() method execute on component refresh. So you’d do something like:

public function updatedKeyword()
{
    doSearch();
} 

This executes your doSearch() method whenever the model keyword is updated which would be debounced at 3000ms. I believe debouncing is based on the keyup event anyway.

edit: function should be public

Hi @shortbrownman

I might haven’t got clear understanding about “debounce”.
The goal I want to achieve is I want to call doSearch function every 2 second.
(I can click 10 keys at once for 1 minute)

And I only use Livewire/AlpineJS, not other external JS for this project.
So I want to limit calling doSearch function using only Livewire.

How can I bind updatedKeyword() method with the Livewire?

If this is your goal, just have your model debounced and rely on the lifecycle hooks to call your doSearch() method.

Here’s a bit more info on Livewire’s lifecycle hooks: https://laravel-livewire.com/docs/2.x/lifecycle-hooks

The premise is that every model has a lifecycle so your keyword model, declared as public $keyword; at the top of your component. It will go through a lifecycle every time it’s changed: hydrate, updating, updated. You can hook into any of these cycles with somewhat magic methods of lifecycle hook + model. In this case, if you want to hook into after the model is updated so you’d hook use the available function of updatedKeyword().

In my original reply, it should be a public function meaning, if you drop:

public function updatedKeyword()
{
    doSearch();
}

into your component code, and have your model in your blade code look like:

<input id="search" name="seach" wire:model.debounce.3000ms="keyword">

it’ll do what you’re looking for.