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

Planning Flexibility Refactor0:00

So in the last video, we managed to get everything working in this Alpine component. So you might think that we're done, but behind the scenes under the hood, the code is actually pretty rigid. It's not flexible at all. We can't specify different event listeners like mouseEnter, and we can't register new directives like x-show easily. So let's make it a little bit more flexible and refactor along the way. Okay, so we're going to do three different refactorings. Let's list them out. The first one is going to be allow custom events, allow other events.

Let's list them out. The first one is going to be allow custom events, allow other events. Second one is going to be allow other directives. And then the third one we're going to make, we're going to actually extract all of this out into a separate function. So extract, sorry, separate object. So we'll have our own dedicated Alpine object, because right now we're throwing all sorts of stuff in the global scope. And that was just sort of to make things easy and not add too much up front. So we're going to extract this all into an object, then we can put it in a separate file.

Scanning Attributes for Events1:21

So how do we do that? Let's say that maybe this existed, this would be great. Like maybe we could say if l has attribute at and then some wildcard, right? But that's not actually possible. Or you might want to be able to pass in some regex like add and then dot, right? Well, that's also not possible. So because we can't use hasAttribute, here's our new strategy. Our new strategy for how we're going to do this, how we're going to find these directives is to list out all of the attributes on an element. So we're walking the DOM, we get to an element.

is to list out all of the attributes on an element. So we're walking the DOM, we get to an element. Right now we're checking for hasAttribute instead, why don't we list out all the attributes on the element, loop through them and check if their name starts with an ad. That's the new strategy. It's a little bit more work. It's a little bit less readable than just hasAttribute, but it's much more flexible. And Alpine and Livewire both work this way rather than rigidly checking for explicit strings. One of the reasons is also when you start adding modifiers like dot window and things

strings. One of the reasons is also when you start adding modifiers like dot window and things like that, it gets really, really cumbersome to do all this hasAttribute stuff. So you'll see what I mean. Let's open up a browser and do a little bit of experimenting. So if I select this element, so right now I want to get this element. Maybe you might use document.querySelector, right? And then maybe it's like button, okay. And then that gets us the button. Now we can access the attributes and experiment.

So if we change it to the adClick, go to the console. Now it's the adClick. Cool. So $zero, and then we'll access the attributes with ->attributes. It's a property on every DOM element where you can get all of the attributes. And then you get this kind of array like thing where you have adClick and then a bunch of stuff. But here's some weird stuff. Like if we just have an array, it looks like an array. It's a little different.

Like if we just have an array, it looks like an array. It's a little different. This is a named node map. It's not just an array. It has an index of zero. So it kind of looks like an array. But there's a bunch of extra weird stuff. So in cases like this, when I have things that kind of should be an array, like I want to use them like an array, I want to do forEach and filter and map, I wrap them in an array, array.from($zero.attributes).

to use them like an array, I want to do forEach and filter and map, I wrap them in an array, array.from($0.attributes). And now I have something that looks a little bit better, just a plain array with items inside of it. And then there's these objects for each attribute. The attribute has a name, which we care about at click, and then the value, which we also care about the expression. Those are the two things we care about. So just to demonstrate, if we forEach through this, which we can do now because we wrapped it in an array before forEach through and get attribute, okay, and then console.log

So just to demonstrate, if we foreach through this, which we can do now because we wrapped it in an array before each through and getAttribute, okay, and then console.log attribute.name. There we go. We have atClick. We can also do attribute.value. And we have atClick and count -- ourExpression. Great. So let's take that knowledge and make a little refactor. So we're going to start with L.attributes.

So let's take that knowledge and make a little refactor. So we're going to start with attributes. Okay. But if you recall, we want to loop through them. So we're going to need to do that array_from and then we'll foreach through each attribute. Okay. And then inside of the foreach, we're going to do an if statement right up front, we're going to say if the attribute's name, so if attribute.name starts with an at symbol, then get the event the name of the event, which is click. So after the at symbol, and to do that, we'll use attribute.name.

then get the event the name of the event, which is click. So after the at symbol, and to do that, we'll use attribute.name. And there's a bunch of different ways to do this sort of thing in JavaScript. There's match if you wanted to use regex, there's split, there's slice. But honestly, the easiest thing for me to remember is replace, like replace at with empty string so much nicer than slice negative one or something that I have to Google, and then I totally forget. So let event equals attribute.name.replace. Alright, so this should give us the event. And then now we can do our listening.

Alright, so this should give us the event. And then now we can do our listening. So we'll paste that in here. Add an event listener. But instead of hard coding, click, we're now listening for that dynamic event that we get out of the attribute, eval with data, and now expression doesn't exist. It's now attribute.value. Okay, get rid of the old stuff. And let's check in. So hopefully, if I roll over this plus sign, it increments.

Reducing Indentation with Guards6:15

Let's make one little more refactor. So right now we have lots of indentation, lots of levels of indentation, it adds overhead to the person reading the code. So as a general rule of thumb, the fewer indentation levels you have, the easier your code is to read. So one really quick way to reduce indentation is to extract methods. So we could extract this into separate functions to clean this up a little bit. But I don't want to waste too much time doing that right now. But here's one refactor that also reduces indentation. It's called a guard clause.

Refactoring to Support Directives7:08

I love this approach because we're saying, hey, if it doesn't start with an @ symbol, we don't care about it, get rid of it. Now that it does, we're going on with business as usual. So we made the refactor, let's check in the browser and make sure that it worked. And it did. Great. So I called it a success. So number one is down allow custom events. Next up, allow other directives. So I want to have something like x-show if count is greater than zero.

Next up, allow other directives. So I want to have something like x-show if count is greater than zero. So whatever this resolves to true or false, I want to show or hide the span tag. And this is going to say, I'm over two, okay, not greater than zero, greater than two. All right. And if we load this in the browser, we just get it there because Alpine hasn't done anything with it. It has no idea the browser has no idea of x-show. So we need to add that. So before we actually make the refactor, we're going to apply two core principles, two principles.

So we need to add that. So before we actually make the refactor, we're going to apply two core principles, two principles that I live by for programming that are really helpful when you're refactoring code. So the first one is designed by wishful thinking. And what I mean by that is, write the API you want to use an API, even that's just fanciness, that's unnecessary. Write the code that you want to exist designing by wishful thinking. So if I were to do that here, right now, if I want to add show, I have to like dig into the code, find where we check, okay, x text, I need to add an else statement. And then every time we add one of these, we're going to have to chain on a new conditional.

the code, find where we check, okay, x text, I need to add an else statement. And then every time we add one of these, we're going to have to chain on a new conditional. And this might get really gross really fast. So if I was designing by wishful thinking, I might have something like directives upfront, where you'd specify the key is the directive, and then the value is a function that accepts the element and the value. So you could do something like this, innerText equals value, because it's isolating the thing that changes. Everything is common among directives, except the name of the directive and what you do with the element in the value.

So let's yank this out this array from attributes, okay, put it in up here. Now we'll get rid of this, and we'll start writing the refactor. So first up, we're going to want to check if the attribute name is in that list of directives. So let's say if Object.keys, if you haven't seen Object.keys, it's really straightforward, and it's very useful in JavaScript. Let's say we have an object foo, bar, Bob, lob, okay? Now Object.keys is going to do kind of what you think it might, it's going to give you an array of all the keys, which allows you to do anything you want to do with arrays, you could map you could forEach. One thing I often do is includes.

you could map you could for each. One thing I often do is includes. So if we say object.keys(data) includes foo, then we're saying yeah, yeah, foo actually exists as a key in this array in php would be something like array_key_exists, but JavaScript doesn't have that. So we have to do object.keys, and then includes, okay. So object.keys directives. Now remember, directives is our list of directives up here, and the key is the directive. So if object.keys includes attribute.name, so if it includes the current attribute that we're looping through, then do our work, but we know better by now, we're going to

So if object.keys includes attribute.name, so if it includes the current attribute that we're looping through, then do our work, but we know better by now, we're going to do that guard clause, right? Okay, now we can carry on, and we'll say, directives. And we can access that key right here attribute.name, because we made sure that it exists. And now we can just access it, and then call it because it's a function. The first parameter is the element. And the second parameter is going to be the evaluated value of the expression. So we val with data, expression doesn't exist, it's attribute.value now. Okay, so hopefully that refactor did it.

Implementing x-show Directive12:13

Now we made the change easy. Now we can make the easy change. You see what I mean here, make the change easy means do the refactor first, then change the thing you want to change. So now, making the change is super easy x show, and we'll say style.display. If value is true, it's going to be block. If value is false, it's going to be none. So I'm just setting some CSS based on the value that comes back. So if it's true, it's going to be block if it's false, it's going to be none, refresh, and now it disappeared.

Extracting Alpine into Object13:00

And the last thing we're going to do is extract this to a separate object. Great. So we'll create a new object, let's say let alpine is equal to an object. And then inside of this, we can declare keys like the first one would be directives, where we pass in these directives. Okay, so there's the directives, we can get rid of that. Now I might want a start function, something I could say alpine.start, that's going to boot it up, it's going to do all of these things. So start here, we need to add commas because we're in an object. And now all this global data, we can put as object context.

So start here, we need to add commas because we're in an object. And now all this global data, we can put as object context. So instead of let root, that's just a global root variable, we can say this.root, instead of let rawData, we would say this.rawData. And then again, with this, these functions are now going to be this.rawData and this. And then these two things here are going to be this, okay. And now we need to basically absorb the rest of this code as methods on this object instead of global functions, and then update all of the global accessors to this. Now before we do this, one thing I like to do when I'm making maybe a big refactor, a

of global functions, and then update all of the global accessors to this dot. Now before we do this, one thing I like to do when I'm making maybe a big refactor, a big structural change, I like my font size and my line heights really big, because it allows the code to breathe and it allows me to zoom in on the code. But for a big refactor like this, I'll generally just bump the code way down so that I can see things at a glance and easily grab chunks and move them around. So rather than forcing you to sit through me adding this dot 100 times, I'm going to put it into fast forward mode. And we'll just settle up when we're done here. Okay, and now we're done.

Totally worked. That's a lot cleaner. And the final step here is to pull this out into a separate file. We'll call it alpine.js. And now we'll rip out all of this code, stick it inside this file. And now the script tag is just going to reference that file. So src equals alpine.js. All right, refresh. We're hovering over, we hit minus. There we go.

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