By @samuel · 2021-10-02 17:11
This is a continuation of my Advanced Livewire series started in the previous post: Advanced Livewire: Traits.
Here we'll talk about working with your components in JavaScript.
Few people know this, but your Livewire components actually have a 1:1 "mirror" in JavaScript — any PHP property that's public is stored on the frontend, and any method that's public can be called from JS. Or more specifically, few people know how structured the JS layer is.
To fetch the frontend instance of a LW component, you can click the element with wire:id
in Chrome dev tools and then use this in the developer console:
$0.__livewire
($0
refers to the last selected element.)
This will return a Component
instance.
If you type:
$0.__livewire.data
You'll see all of the component data.
And similarly, if you use:
$0.__livewire.call('foo', 'bar');
This will call foo('bar')
in PHP — using an HTTP request. The method itself returns a promise and its return value is passed to the then()
callback.
This has both cool use cases and potential security issues when you're not aware of this. So always make sure that your public properties and methods really should be public.
Here's an example of how the returned promise can be used:
public function slug(string $text): string
{
return Str::slug($text);
}
$0.__livewire.call('slug', 'foo bar').then(text => console.log(text));
// prints 'foo-bar' in the developer console
The code above is useful, but also kind of ugly. For that reason, we will use the proxy created for Alpine. Odds are that we'll be using Alpine anyway, so this makes things even simpler. To access the proxy, use:
$0.__livewire.$wire;
The proxy lets you access properties like this:
$0.__livewire.$wire.foo;
Set them like this:
$0.__livewire.$wire.foo = 'bar';
And call methods like this:
$0.__livewire.$wire.foo('bar');
From here on we'll assume that we're specifically in Alpine context, so we'll just use $wire
in Alpine to access this proxy.
You can access $wire
like any other Alpine property:
<div x-data>
<button @click="$wire.foo = 'bar'">Make it bar</button>
</div>
In methods we use this
, again like for any other Alpine properties:
<div x-data="{
foo: '',
bar() {
this.$wire.foo(this.foo);
},
}">
<input x-model="foo">
<button @click="bar">Bar</button>
</div>
When you submit a reply, this method gets executed:
reply() {
$wire.reply().then(() => this.replying = ! $wire.valid)
},
And similarly, the edit profile component looks like this:
<aside x-data="{
editing: false,
save() {
$wire.save().then(() => this.editing = ! $wire.valid)
},
}">
<form
x-on:submit.prevent="save()"
class="overflow-hidden bg-white rounded-lg shadow"
wire:loading.class="cursor-wait"
>
...
</form>
<form>
The benefit of using slightly more intelligent Alpine logic is that the UI interactions feel much smoother.
In this specific example, the UI works in these steps:
We use the same logic on the edit profile page:
<aside x-data="{
editing: false,
save() {
$wire.save().then(() => this.editing = ! $wire.valid)
},
}">
<form
x-on:submit.prevent="save()"
class="overflow-hidden bg-white rounded-lg shadow"
>
...
</form>
</aside>
And that's it for this week's Advanced Livewire. I have decided to make it a weekly series, so make sure to check back next week. (Or see the newsletter, since it's also getting sent in each issue of our weekly newsletter.)
See you next week!
By @wizjo · 2022-06-06 05:47 (edited)
What is benefit of doing like this, instead of using wire:click="save" and entangling "editing" value? Or is the aim to show that you can, not necessary that it is better?
By @samuel · 2022-06-06 07:48
Yeah, the point is to show that it can be used. In some contexts it is better and it lets you do things you couldn't do otherwise. And most importantly, it makes you aware of the security implications — that any of your public methods or properties can be accessed from the frontend, even if you don't expose them in Blade.
By @wizjo · 2022-06-06 07:59
Thank you and hope you will cover this topic (this special cases where we can benefit from calling components this way) and show some examples in future episodes =).
By @abrardev99 · 2021-10-04 07:19
Great read.
By @lars · 2021-10-02 19:01
Great stuff, adding some additional (but not required JS) can really improve the UX in some cases! Good example 🙌🏼
By @boris · 2021-10-02 17:28
Great read! Is $0
a Livewire-specific feature?
By @samuel · 2021-10-02 17:39
It's not! That's just the last element you clicked on.
See here:
It's like right clicking an element in the Elements tab and selecting "Store as a global variable", except you can save those clicks if you just need to work with the last element.
$0
is the last one, $1
is the previous one, $2
is the one selected before the previous one, etc.
By @wali · 2021-10-13 14:46
In cases when you have accessed devtools via shortcut or via browser menu.
$0
will refer to the body
element