Rethinking Web Complexity0:00
You know, modern web development is an exercise in complexity. There's actually a lot of words I want to use there, but complexity just kind of encompasses everything. And in my opinion, it's complex for the sake of being complex. I'm not going to go into why I think that, because this is a topic that's very near and dear to my heart, and I can spend a lot of time talking about it. But it is complex. There is no question about that. We generate content on the client using JavaScript and JSX, and the server is just relegated to really a glorified ORM. It's an API that we fetch data from so that we can generate our content in JavaScript. But it doesn't have to be that complex.
It's an API that we fetch data from so that we can generate our content in JavaScript. But it doesn't have to be that complex. We can use the server for what it was actually designed for, that is processing data and generating content, because it is very good at generating content. And then we can use JavaScript to fetch that content and add any other interactivity that we would normally want to include with Vue or React or things like that, but without using Vue or React or things like that, because again, it's complicated. So we have tools to make all of this work very well. And one of those tools is HTMX. Now, HTMX itself is not new. It's been around for a couple of years.
HTMX Attribute Basics1:21
And one of those tools is HTMX. Now, HTMX itself is not new. It's been around for a couple of years. And the idea behind HTMX has been around for a very long time. And the idea is very simple. We generate content on the server, we fetch that content with JavaScript, and then we just add whatever interactivity that we need with either just plain vanilla JavaScript or we could use something like Alpine. Now, when it comes to fetching content, HTMX has us covered. In fact, the overall idea is that we use HTML attributes. So in this case, whenever we click on this button, we're going to make a POST request to this URL.
In fact, the overall idea is that we use HTML attributes. So in this case, whenever we click on this button, we're going to make a POST request to this URL. We're going to fetch the content there. Then we are going to swap this button with whatever content came back from the server. And that is being controlled by this HX swap set to outerHTML. And that's it. That's all there is to it. It's a different way of thinking, especially compared to modern web development. So let's take a look at my little project that I have. So this is a smaller part of a web application where we would issue payments for invoices. So this is a dashboard where we have what I call a snapshot of all of the open invoices.
Dashboard Goal and UX2:28
So this is a smaller part of a web application where we would issue payments for invoices. So this is a dashboard where we have what I call a snapshot of all of the open invoices. These are the invoices waiting to be approved. Then we have a snapshot of the approved invoices. So these are ready to be paid. And we can click on these links. It goes to individual pages to work with the open invoices or the approved invoices. And these forms work. So let's see, this is approved invoices. If we wanted to select a couple of these for payments, then we could do that, submit that.
So let's see, this is approved invoices. If we wanted to select a couple of these for payments, then we could do that, submit that. And then boom, they are submitted for payment. And while that works, I do want to have a feel of a modern application. So on our index here, this dashboard, whenever we click on one of these links, I don't want it to go to the individual page for the open or approved invoices. Instead, I want that data to be loaded here. Now, I still want the existing functionality. I want to be able to go to these pages if that's what I want to do. But I still want, well, I want to have my cake and eat it too,
Installing HTMX with Vite3:33
I want to be able to go to these pages if that's what I want to do. But I still want, well, I want to have my cake and eat it too, which I've never really fully understood that saying, but that's what I want. I want to be able to go to the individual page if that's what I want. But I also want to load this content here. So HTMX is going to make this all possible. But the first thing we need to do is install HTMX. And we can do that in a variety of different ways. Now, I'm using Vite for all of my asset bundling. So I'm just going to install HTMX as an npm package,
Now, I'm using Vite for all of my asset bundling. So I'm just going to install HTMX as an npm package, so that then I can go to bootstrap.js. And I can essentially make it globally accessible like what they did with Axios here. So we're going to have import htmx from 'htmx.org'. And then we will set window.htmx to htmx. And before we do anything else, let's just make sure that that is working. So let's go to the browser. Let's go to the console. HTMX.
Fetching and Swapping Content4:31
Let's go to the console. HTMX. Yes, we have an object. So we're good to go. So the first thing we need to do is fetch the data from the server whenever we click on this open invoices link. And we can do that by using HTMX. We need to open up the index view. I do have some Blade components like a layout, a section, and a snapshot. We don't really necessarily need to be concerned with the markup for those components yet.
I do have some Blade components like a layout, a section, and a snapshot. We don't really necessarily need to be concerned with the markup for those components yet. But for right now, we just want to focus on this a element here. So of course, it has an href attribute. And there's nothing else because it's just a simple link. But we want to use HTMX to dynamically fetch that content so that we can insert that into the page. And we will do so using an attribute, hx-get. So the idea here is that we are going to make a GET request for our URL that will fetch the content and that will insert that into the page.
So the idea here is that we are going to make a GET request for our URL that will fetch the content and that will insert that into the page. So we can actually see this in action. Whenever we click on open invoices, it worked. It's not exactly what we wanted, but it did in fact fetch that content and insert that into the page. But let's see what happened here. So let's inspect that link. And we can see here we have our a element. There's nothing out of the ordinary here. Everything is fine.
There's nothing out of the ordinary here. Everything is fine. Whenever we click on the open invoices, we can see that, well, the link changed. The outer HTML is basically the same, but the inner HTML is completely different. It fetched our open invoices page and it replaced the inner HTML with that content. But it's the entire page. We can see here's the Laracast logo. If we take a look at the open invoice page, you know, that's the header that's at the very top. So there's a couple of things here. First of all, we want to change where our content is going to be inserted.
So there's a couple of things here. First of all, we want to change where our content is going to be inserted. But then we also want to truncate the content itself because we don't need this entire page. We just basically need this right here. So let's take it one step at a time. Let's first of all change where we are going to insert this content. And we can do that with another attribute called hxTarget. We give it a CSS selector. So we can say that we would have an invoiceList ID. So we don't have this here, but we can easily add it.
So we can say that we would have an invoiceList ID. So we don't have this here, but we can easily add it. We can add a div that has an ID of invoiceList. And whenever we go back to the browser and we click on our link, we can see that the content is being loaded there. And that's great and all, but let's take a look. So we have our section that is our snapshots here. Then we have our div element that we just created with the invoiceList. And we can see that the content is inside of there. But really what I want to do is replace this div element.
And we can see that the content is inside of there. But really what I want to do is replace this div element. So I too want to use the outer HTML for the swap. So we will use that hxSwap. The default is inner HTML, so we're going to change it to outer HTML. And let's go back. Whenever we click on open invoices, we can see that the div element is replaced. We have the header and then the main. Those are coming from the content of our open invoices. So as far as inserting that contents, we're doing what we want.
Returning Laravel View Fragments7:45
Those are coming from the content of our open invoices. So as far as inserting that contents, we're doing what we want. Now we just need to truncate the data that's coming from the server. And we can do that by using Laravel's fragment feature. So we need to open up lists.blade.php. This is the view for the invoice list. We can see here's our form. Here's our submit button. And then we have our table. And so as far as our content is concerned, we don't want the layout.
And then we have our table. And so as far as our content is concerned, we don't want the layout. We want our fragment here. And let's just give it a name of invoiceList. The name itself is arbitrary. We can set that to whatever we want. It just makes sense to call this invoiceList. Then we need to add the end fragment directive at the end of that section. And so now that we have defined our fragment, we just need to load just that fragment whenever HTMX makes its request.
And so now that we have defined our fragment, we just need to load just that fragment whenever HTMX makes its request. So then the question is, how do we know when a request is just a normal request or an HTMX request? Well, we can figure that out. Let's go to the Network tab and let's just click on Open Invoices. We will see a request it initiated from HTMX. So we know that HTMX made this request for us. So let's inspect this. We can scroll on down.
So let's inspect this. We can scroll on down. We don't need the response headers, but we do want to look at the request headers. And everything is normal until we get to this point here. We have three headers that begin with HX. And we could probably use any one of these three, but really the one that we want is this HX-Request. So if the request includes this header, we can assume that HTMX made the request, so we want to truncate the content to just our fragment.
we can assume that HTMX made the request, so we want to truncate the content to just our fragment. So we can do that inside of our InvoiceController, where I have two methods for showing the approved invoices and then showing the open invoices. All these do is call this other method called showInvoicesByStatus. So let's look at that here. We can see here we are fetching the invoices by the given status, then we are returning the view. But here, we want to return the view, and
then we are returning the view. But here, we want to return the view, and then we will call the fragmentIf method. This allows us to test a condition so that if the request has a header of HX-Request, then we want the specified fragment, which in this case would be invoiceList. So for normal requests, if we just go to this link, we see that we get the entire response, that's fine. But if we just click on the link, HTMX is going to make that request, our controller is going to determine whether or not to return just the fragment.
But if we just click on the link, HTMX is going to make that request, our controller is going to determine whether or not to return just the fragment. And in this case, it returns just the fragment. We no longer have the entire HTML response. So now we just need to do the same thing for the other link. So we can go to the index link. Let's just copy all of this. And for the approved link, we will paste that in. We of course need to change the URL for the HX get, but that's it. So if we go back to the browser, if we click on open invoices,
We of course need to change the URL for the hx-get, but that's it. So if we go back to the browser, if we click on open invoices, we see our open invoices. If we click on approved invoices, we see approved invoices. And these forms should still work. If we inspect the form, we should see that this is posting to the approved URL. But if we do that right now, it's going to, of course, make that request, but it's going to take us to the approved page, because HTMX is not set up yet for our form. But that's something that we can tackle.
because HTMX is not set up yet for our form. But that's something that we can tackle. So there's a couple of things to note here. The first is the behavior of our element, our A elements here. So this is a link, of course. The default behavior of whenever you click on the link is that it navigates the browser to whatever was specified in the href attribute. Well, by adding the hx-get attribute to that link, we have now essentially disabled that default action. So now the link is not going to navigate the browser to that URL,
we have now essentially disabled that default action. So now the link is not going to navigate the browser to that URL, unless if we right-click on it and then choose to open that up into another tab. So whenever you use an HTMX attribute on an element, it is going to prevent the default action from occurring. And that's typically what you would want. The second thing to note is the invoiceList. So the target that we set for our content is this div with an ID of invoiceList. Now, we know that, but if we take a look at the list view,
our content is this div with an ID of invoice_list. Now, we know that, but if we take a look at the list_view, we can see that the section that we are pulling in also has an ID of invoice_list. And that is why being able to go in between open invoices and approved invoices is working. Because by clicking on any one of these links, we are inserting another element that has that same exact ID. So if we were to change this, let's just say invoice_list_2, and we click on any one of these links, it's going to work the first time. But it's not going to work the second time,
we click on any one of these links, it's going to work the first time. But it's not going to work the second time, because there's no longer an element with an ID of invoiceList. So if you want this kind of functionality, you have to think in those terms. You have to always be sure that your target element is available within the page. So now that we have this functionality set up and ready to go, we need to focus on making sure that the form works with HTMX. And we will look at how to do that in the next episode.
