تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Implementing wire:model0:00

Folks, it is wire:model time. We have this AddTodo component and unfortunately it doesn't completely work. We have wire:click working, but wire:model does not work. If we look at our Todos component here, we have an input element with wire:model and we set it to draft. And we expect that as we type into the input, the draft property is going to get updated with whatever we type in. So let's tackle that. Let's open up our Livewire JavaScript and we have a init wire:click. Let's also init wire:model.

Let's open up our Livewire JavaScript and we have a wire:click. Let's also a wire:model. Okay. And we'll create a function called wire:model. Great. And now inside of here, we're going to do some work. Let's talk about what we need to do first. There's actually two things we need to do. There's setting the data from the server immediately when the page loads, and then there's setting it or sending it to the server every time we type in.

There's setting the data from the server immediately when the page loads, and then there's setting it or sending it to the server every time we type in. What do I mean by that? So if we open up this Todo component and I type something in the draft, like some to do's. Okay. When I refresh this page, you would expect that the input is set with some to do's, but it's not. So that's the first thing we need to tackle with wire:model. And then the second thing we need to tackle is as we type in, we send the proper data.

So that's the first thing we need to tackle with wire:model. And then the second thing we need to tackle is as we type in, we send the proper data to the server. Right? Okay. So let's put some comments here. First, we need to set input values and we got to spell values properly. That's really important. So set input values and then send new input values or whatever. I don't know.

Initialize Input Values1:25

So set input values and then send new input values or whatever. I don't know. Okay. So for set input values, let's get all the elements in the component that have wire:model on them. And let's find the property in this case draft in the snapshots that we took when we loaded the page we took, we extracted all the data from the components and we'll set those input values to that data. So I'm going to copy this bit of code up here. And what this does is it gets all the elements with wire:snapshot.

So I'm going to copy this bit of code up here. And what this does is it gets all the elements with wire:snapshot. We want to get all the ones with wire:model, but this is going to get them all on the page. So if you have like multiple components, this is going to get all of them in the foreach where we don't want that. We want to actually only get the wire:models inside of the root element. So this is a nice feature you may or may not know about in JavaScript, but you can call querySelector on a specific element and it's going to scope it to that element. It's just extremely handy little feature. I love it.

It's just extremely handy little feature. I love it. All right. So we get all the wire:model elements inside the component and we have them as this temporary $l variable, which is not good because we're using it up here. So if we wanted to use $l inside of this callback, then it would be this one and not this one. So I'm going to rename this to $i for iteration. It's a little thing I do sometimes when I don't need to use the variable that much inside of a callback and I don't want to mess with overriding scope. So $i is a little shorthand for iteration, I suppose.

of a callback and I don't want to mess with overriding scope. So i is a little shorthand for iteration, I suppose. Okay. So the first thing we need to do is get the property that we want to set the property name. So in this case draft. So let's do that. Let's say let property equals and then i.getAttribute wire:model and let's alert that and make sure we have the right one. So if we alert this property, we refresh and there we go.

and make sure we have the right one. So if we alert this property, we refresh and there we go. We have draft. Great. Okay. So the second thing now we're going to actually do the thing where we get the element and we set its value. So every element or every input element, and that goes for not just inputs, but like select elements and checkboxes and radio inputs and all of those input E kind of elements, they all have this value property on them and it's just in browsers

input, select elements and check boxes and radio inputs and all of those input type elements, they all have this value property on them and it's just in browsers and it's both a getter and a setter. So if we, you could like console.log, let's just alert this value here. So alert i.value, it's going to be an empty string. So you don't see it, but yeah, whatever, it's an empty string, but you can also set it and it's going to set the value of the input. So if we do i.value equals foo and then refresh the page, there we go. foo is now set as the input value, but we don't want to set it to foo. Obviously we want to set it to the draft property.

Foo is now set as the input value, but we don't want to set it to foo. Obviously we want to set it to the draft property. So this is draft and we need to get that from the data. So let's re familiarize ourselves with Livewire. That's the snapshot we got from the component. So when this page loads, I'm just going to console.log this just so we can see what it is in the browser. All right. Looking at the console unexpected token. What did I do wrong?

Looking at the console unexpected token. What did I do wrong? Um, I have this half finished line of code and so it's mad about that. Okay, here we go. We have this object that has the class and then the data of the component. So we want the data and when you, and we want to access a property on that data called draft. So let's do this. Let's say let data equals, and then we'll let this equal L.__livewire.data.

livewire.data. Okay. And then here, when we set l.value, we can set it to data and then the property key. Not actually data.property. It's data.draft. I don't know if you've seen this syntax, but we want to get this, but this is a variable and we can't just say, you know, data.property because it thinks this is a hard coded key and not, um, a variable, but you can use this array syntax on an object to dynamically get a property. All right.

dynamically get a property. All right. So let's refresh. And with any luck, there we go. Check it out. We have some to-dos. Perfect. In the input. So that's the first step. That's setting the input values for wire:models.

Send Updates on Input5:20

So that's the first step. That's setting the input values for wire:model. The second one is sending new input values as a User types into an input field. So we're going to do that very similar to how we listened for a click for any element that has wire:click on it. We're now going to do that, but for wire:model. So we'll listen for an input event, which I'll explain that in a second. And then if the element doesn't have wire:model, kick it out of the callback. We don't want it. Um, and then we'll also here, we'll say property and wire:model.

We don't want it. Um, and then we'll also here, we'll say property and wire:model. Okay. And then we'll, we'll stop there. So why did I set this to input? Well, obviously we don't want to listen for a click. And you might think like, well, why aren't you listening for a keyDown or a keyPress or keyUp or something like that? And we could, and that would actually work where every time the user types in and presses a key, a network request gets sent to the server to update draft to this new string.

And we could, and that would actually work where every time the user types in and presses a key, a network request gets sent to the server to update draft to this new string. But what if we want to do wire:model on a, I don't know, let's say a select dropdown or a checkbox or something where you're not pressing a key, but the input value has changed. So browsers give you this nice little input event and also a change event. They both behave very similarly and I'll show you the difference kind of later on, but we're going to listen to input event because it's going to get fired from every single input type element. And then we can basically use e.target.value, that value getter and setter to set and get the values of any of these inputs.

And then we can basically use event.target.value, that value getter and setter to set and get the values of any of these inputs. And we don't care if it's a text input, we don't care if it's a text area or a select dropdown. So that's just something that we're going to do. Okay. So let's walk through this logic. We say, Hey, an input event just happened inside the component. Did this input event come from an element with wire:model on it? If it didn't, then kick it out.

Did this input event come from an element with wire:model on it? If it didn't, then kick it out. We don't care about it. If it did, then let's get the property that we want to set, and this is going to be draft and let's also get the value. So remember event.target is the element that fired off the event, which in our case is that input element and value. Like I said, it's both a setter and it's a getter. So this will get the new value that we just typed in. We'll put this in a variable called value.

So this will get the new value that we just typed in. We'll put this in a variable called value. And now we have the two pieces of data that we need to send to the server. So we use the sendRequest, just like we did in wire:click. We're saying element, and now we don't want to say callMethod. We're going to create a new action, I suppose, to send to the server, and it's going to be called updateProperty. Okay. And then we don't just want to send the property name. We want the property name and value.

So we get the data and then we send it to the server with this update property bit. Now the server doesn't know how to handle this, but let's just make sure that this all works in the browser. We're going to load up the network requests. And if I type in, I'm going to type an L here, or maybe I'll type in an X. Okay. Okay, great. So we typed an X and because we had that input event listener, we sent a request to the server. And let's take a look at what got sent the snapshot and the draft is some toDos.

the server. And let's take a look at what got sent the snapshot and the draft is some to dues. And then we have this update property key where we're saying, Hey, update the draft property to some to dues and then X and look at what data came back from the server data draft some to dues. So of course this didn't actually handle it. It didn't set it to X, but we know that when we implement it, if we get some to dues X back from the server, then we did everything we need on the server. So let's implement this. We'll go into our routes file, and this is our Livewire endpoint that handles all Livewire

Handle updateProperty Server-Side9:02

So let's implement this. We'll go into our routes file, and this is our Livewire endpoint that handles all Livewire requests. And here's that bit of code where we get the call method out of the request and actually call a method on the component. And let's duplicate this. And now we're going to look for updateProperty. So let's do updateProperty. And so if an updateProperty key exists, we want to call an updateProperty method on our Livewire class thing and pass it the component, the property and the value.

And so if an updatePropertyKey exists, we want to call an updateProperty method on our Livewire class thing and pass it the component, the property and the value. And to get these two things, we can get them out of this request here using that fancy array destructuring assignment thing that I told you about earlier, where we can, you know, we know that we're passing a tuple. We know that updateProperty is a tuple, which is just a plain array with two little items in it. So we can get those out with this array assignment right here, and do it directly in the if statement. This is super elegant. And I just want to say that I love that php can do this kind of inline assignment, so

This is super elegant. And I just want to say that I love that php can do this kind of inline assignment, so that you're saying, hey, if there's a request, a key called callMethod, then do this thing. But instead of re getting this request callMethod, you can set it to this temporary variable, and then use it. This is something that JavaScript can't do, JavaScript can do so much. And it's, it's not that often that I'm in php, and I go, wow, this is so much better than JavaScript or something like that. But this, this is one thing that I wish JavaScript could do, and php can do so great, we're calling updateProperty, we're passing it the data we need.

But this, this is one thing that I wish JavaScript could do, and PHP can do so great, we're calling updateProperty, we're passing it the data we need. So let's go into our Livewire PHP file, scroll down to callMethod, and we'll duplicate callMethod. And now we'll call this updateProperty. Okay, so updateProperty. Great. And then instead of just passing in the method, we pass in the property and the value. And then instead of calling this method, we access the property and set it to the value. So basic straightforward PHP stuff right here.

And then instead of calling this method, we access property and set it to the value. So basic straightforward php stuff right here. All right, let's refresh the page, look at the network requests, type this x. And now with any luck, we look at the snapshot that came back and look at the data, some to do's under ... x. Perfect. That's exactly what we want. So if we hit add to do, check it out, it worked, we added our some to do's ... x. But there's one little bit that doesn't work properly. We hit this.

Sync Inputs After Requests11:24

But there's one little bit that doesn't work properly. We hit this. And yeah, okay, so the the draft got set to an empty string, but the input didn't update. Because if you think about it, we only update all the wire:models when we load the page, we also need to add some code to update the wire:models after every Livewire request, because you never know what kind of Livewire request is going to change a property that an input depends on. So let's do that. Let's open up our JavaScript. And there's this bit here, this where we look for all the wire:models and set their data.

Okay, that still works. So the fix here is that anytime we send any form of request to the server, when it comes back, we want to call this method, not just in wiredModel. So fortunately, we extracted out the sendRequest function. So let's just go into there. And then here's our then this is where we do everything with the response from the server. We're just going to call updateWireModelInputs. All right, refresh. And now if I hit addTodo check it out, it worked new todo, okay, add and then it worked. So this is good wiredModel is basically done.

Fix Middleware and Optimize13:01

And now if I hit add to do check it out, it worked new to do, okay, add and then it worked. So this is good wire:model is basically done. But we did just notice a bug. Did you catch that? When I typed in two words, new space, it doesn't let me type in a space. And why is that? Well, I'll save you the deep diving. Basically every level app in kernel.php, which is inside your app folder, HTTP, and then kernel. This is where all the middlewares are stored.

then kernel. This is where all the middlewares are stored. There's a middleware called trimStrings that basically anytime you send a string to the back end with extra spaces on it, it will trim those automatically. So if we type new and then space, it's going to trim it before it gets into our code. And so then when it comes back, it's going to reset the input and wipe that space, which is a bummer. So a Livewire itself is actually really intelligent about this, but we're just going to remove it from the whole layer of the app entirely. And there's actually another small little one here that I don't like convertEmptyStrings

to remove it from the whole layer of the app entirely. And there's actually another small little one here that I don't like convert empty strings to null. So if I remove all of this, take a look at what is in the snapshot, it's now draft is equal to null, but we actually want draft to be an empty string because it's a string. We set it to an empty string initially. So let's get rid of convert empty strings to null. Those aren't middlewares that are actually convenient when you're building a Laravel app. They're helpful to have, but in our case, they don't help us.

And that is just too much. You don't want that as a user's typing and you don't want to send 200 requests if they type a small paragraph. So Livewire, there's a few things that Livewire does to mitigate this. The first is there's a debounce added by default in Livewire, which basically means if I type this in real Livewire, because I typed it so fast, it is going to wait until my fingers take a break and then it's going to send the request. So that would only send one request in real Livewire, but it doesn't fix all the server issues because if I type, you know, slowly, it's still going to send a bunch of these little requests, maybe a handful for just a small input field that maybe you don't need.

issues because if I type, you know, slowly, it's still going to send a bunch of these little requests, maybe a handful for just a small input field that maybe you don't need it to be so live. You know, if that's the case in Livewire, you may or may not have seen the syntax. There's a syntax called wire:model.lazy. Not module. I don't know why I did that. If you use wire:model.lazy instead of normal wire:model, what's going to happen is it's going to use, it's going to listen for the change event instead of the input event.

is it's going to use, it's going to listen for the change event instead of the input event. And what does that do? Well, it basically input fires every time you type a keystroke change fires every time you leave the input or blur it, whatever. So change is actually a better event to listen to whenever you're listening to input events, because it doesn't happen so frequently. So in Livewire hot tip, use wire:model.lazy where you can, because most of the time you're better off with it and it's going to save a bunch of server resources. So that's basically that for gotchas.

There we go. It's an empty string. Now, if you want, get out a pen and paper and maybe that's a bonus quiz for you. Figure out why that happened, because you can just logically figure it out because this click happens when your mouse comes up and this input gets fired when your mouse goes down. Um, whatever. Livewire takes care of all these things and uh, yeah, and we just, it's completely out of the scope of this series to tackle those deep, deep problems like that. But I just wanted to give you an idea of how wire:model works, get some basic implementation

wire:modelwire:model.lazy

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟