Skip to content

Commit ac1dbf4

Browse files
committed
Fixes for tiny problems
1 parent ab0777d commit ac1dbf4

File tree

3 files changed

+68
-21
lines changed

3 files changed

+68
-21
lines changed

_posts/2025-01-15-htmx-todo-tutorial-II.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Let's create a `HTTP POST` route handler like this, just to see that we got some
115115
// Auth Routes
116116
app.post("/auth/login", async (req, res) => {
117117
const { credential } = req.body;
118-
console.log({req.body});
118+
console.log(req.body);
119119
});
120120
```
121121

@@ -204,10 +204,10 @@ Now we configure our Express application to use the session:
204204
// at the top - import the functions
205205
import { sessionConfig, sessionTimeoutMiddleware } from "./middleware/session.js";
206206

207-
// At the end of the middleware section - configure the sesions
207+
// At the end of the middleware section - configure the sessions
208208
// Configure sessions
209-
app.use(appSession.config);
210-
app.use(appSession.sessionTimeout);
209+
app.use(sessionConfig);
210+
app.use(sessionTimeoutMiddleware);
211211
```
212212

213213
We have configured our session handling.
@@ -348,6 +348,12 @@ app.get("/auth/logout", (req, res) => {
348348
349349
We simply destroy the session and return a re-rendered home page.
350350
351+
## WHY DO I HAVE TO LOGIN IN AND OUT ALL THE TIME!?
352+
353+
When we restart the server in development mode, `npm run dev` we are also, through our session configuration killing the session. Hence we have to log in every time that happens.
354+
355+
You can get around this by storing the session in a persistent store, but I have just left that here.
356+
351357
## First HTMx attributes
352358
353359
And we also got to use our first HTMx attributes; `hx-get` and `hx-target`. Let me give a short introduction, but dive deeper in the next post.

_posts/2025-01-16-htmx-todo-tutorial-III.md

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ service cloud.firestore {
3838

3939
### Configuration application
4040

41-
We will use the firebase SDK to access Firestore, so install it with `npm i firestore`.
41+
We will use the firebase SDK to access Firestore, so install it with `npm i firebase`.
4242

4343
Then create a firebase app, by configuring it like this (I've put this in `lib/firebase/config.js`):
4444

4545
```javascript
4646
import { initializeApp } from "firebase/app";
47+
import { getFirestore } from 'firebase/firestore';
48+
import { getAuth } from 'firebase/auth';
4749

4850
const firebaseConfig = {
4951
apiKey: process.env.FIREBASE_APIKEY,
@@ -54,7 +56,10 @@ const firebaseConfig = {
5456
appId: process.env.FIREBASE_APPID,
5557
};
5658

57-
export default const app = initializeApp(firebaseConfig);
59+
const app = initializeApp(firebaseConfig);
60+
61+
export const db = getFirestore(app);
62+
export const auth = getAuth(app);
5863
```
5964

6065
Yes. Let's play it safe and put all those keys in the `.env` file.
@@ -215,7 +220,7 @@ First let's create the todo-routes in a separate file (`routes/todo.js`), and cr
215220
import express from "express";
216221
const router = express.Router();
217222

218-
router.get("/new", (req, res) => res.render("/todo/new.ejs"));
223+
router.get("/new", (req, res) => res.render("todo/new.ejs"));
219224

220225
export default router;
221226
```
@@ -239,7 +244,7 @@ Then create the form in `views/todo/new.ejs`:
239244

240245
We'll soon add in the HTMx attributes.
241246

242-
Let's clean up the `main.ejs` and make the main section look like this:
247+
Let's clean up the `main.ejs`, make it a `<main>`-section, take out the hard-coded data and make the main section look like this:
243248

244249
```html
245250
<main class="todo-container">
@@ -252,6 +257,8 @@ Let's clean up the `main.ejs` and make the main section look like this:
252257
</main>
253258
```
254259

260+
Yes - that's all we need. Later our todo-data will be injected into the `todo-list` using HTMX.
261+
255262
Notice the `hx-trigger="load"` that will issue a `HTTP GET` towards `/todo/new` when the div is loaded. That will then lazily load the list.
256263

257264
Also notice the `<div id="todo-list">` which will be the place where the todo list is generated.
@@ -278,7 +285,7 @@ Let's set up the form and button to issue the requests in the proper way :
278285
```
279286

280287
* With the help `hx-post`, `hx-target` and `hx-swap` we have told HTMx to post the form (it will automatically pick up all inputs in the form), and add the result to `#todo-list`
281-
* HTMx has a [rich event system](https://htmx.org/attributes/hx-on/) that we can hook into. Here we are getting called after the request and reset the form.
288+
* HTMx has a [rich event system](https://htmx.org/attributes/hx-on/) that we can hook into, using various `hx-on`-attributes. Here we using `hx-on::after-request` to get called after the request and reset the form. Yes, using some JavaScript, although we are using HTMx (the framework that promise that you shouldn't write any JavaScript ... sue me).
282289

283290
Let's now build the backend that stores the todo in firestore and return a snippet of html to insert in `#todo-list`
284291

@@ -307,15 +314,15 @@ And the `todo/todo-list-item.ejs` can look like this:
307314
</div>
308315
```
309316

310-
(I made some styling changes here)
317+
(I made some styling changes here, that you can steal from the [finished style-sheet](https://github.com/marcusoftnet/htmx-todo-tutorial/blob/main/public/style.css))
311318

312319
Perfect we can now create new todo items. AND they get prepended to the list. However, if you reload the page the list is gone.
313320

314321
### Lazy load the list of todos
315322

316323
That's because we are using HTMx capabilities to dynamically update the list, on the client. However, we have never loaded the list of items from the start (or when the page is reloaded).
317324

318-
But we have everything we know to do that. Let's lazily load the list. Here's the backend in `routes/todo.js`:
325+
But we have everything we need to do that, in place (just about). Let's lazily load the list. Here's the backend in `routes/todo.js`:
319326

320327
```javascript
321328
import { addTodo, getAllTodos } from "../lib/firebase/todoService.js";
@@ -371,7 +378,7 @@ Restart the application and log in and you can now see the different routes bein
371378

372379
### Toggle completion
373380

374-
Toggle completion will be implemented with a `HTTP PUT` and then I'll just replace the entire `#todo-item`.
381+
Toggle completion will be implemented with a `HTTP PUT` and then I'll just replace the entire `#todo-item`. Update the `todo-list-item.ejs` to look like this:
375382

376383
```html
377384
<div class="todo-item">
@@ -399,6 +406,8 @@ router.put("/:id/toggle", async (req, res) => {
399406
});
400407
```
401408

409+
(Don't forget to import `toggleTodoCompleted` and `getTodo` from the `todoService.js`)
410+
402411
The `:id` construct is a way for Express to parse the querystring into `req.params.id`.
403412

404413
### Delete Todo items
@@ -414,7 +423,7 @@ router.delete("/:id", async (req, res) => {
414423
});
415424
```
416425

417-
The correct way, according to REST principles, to respond to `HTTP DEL` is through `No Content` (status code 200 or 204). I've always wonder about that, but now it makes sense. Let's replace this element itself with the empty response, in effect deleting the the element.
426+
The correct way, according to REST principles, to respond to `HTTP DEL` is through `No Content` (status code 200 or 204, but I ran into problems returning 204). I've always wonder about that, but now it makes sense. Let's replace this element itself with the empty response, in effect deleting the the element.
418427

419428
In the template I also show off the `hx-confirm` that will pop-up a confirmation box.
420429

@@ -467,6 +476,8 @@ Here's the cleaned up code:
467476

468477
Nice! We're getting close to done. Let's make a Edit-feature too.
469478

479+
(Again - I added some styling here to better see that the icons are clickable - get your [styles here](https://github.com/marcusoftnet/htmx-todo-tutorial/blob/main/public/style.css))
480+
470481
### Edit todo item
471482

472483
This is a bit trickier - we need a form to edit the item, and then a way to update the item using a `HTTP PUT`.
@@ -487,9 +498,31 @@ router.put("/:id", async (req, res) => {
487498
});
488499
```
489500

490-
Displaying the edit form is a little bit interesting. We're going to change
501+
First we update the `todo-list-item.ejs` to include a way to call the `/todo/<id>/edit` route and display the `edit.ejs´ form:
491502

492-
And the `edit.ejs` file looks different enough from the `new.ejs` to warren a separate file, but it's quite similar:
503+
```html
504+
<div class="todo-item" hx-target="closest .todo-item" hx-swap="outerHTML">
505+
<span class="todo-title"><%= todo.title %></span>
506+
<span class="todo-field"><%= todo.duedate %></span>
507+
<span class="todo-field todo-action" hx-put="/todo/<%= todo.id %>/toggle"
508+
><%= todo.completed ? "✅" : "⏳" %></span
509+
>
510+
<span
511+
class="todo-field todo-action"
512+
hx-get="/todo/<%= todo.id %>/edit"
513+
hx-swap="innerHTML"
514+
>...</span
515+
>
516+
<span
517+
class="todo-field todo-action"
518+
hx-confirm="Delete?! Sure about that?"
519+
hx-delete="/todo/<%= todo.id %>"
520+
>❌</span
521+
>
522+
</div>
523+
```
524+
525+
The `edit.ejs` file looks different enough from the `new.ejs` to call for a separate file, but it's quite similar:
493526

494527
```html
495528
<form id="new-todo-form">
@@ -504,6 +537,10 @@ And the `edit.ejs` file looks different enough from the `new.ejs` to warren a se
504537
</form>
505538
```
506539

540+
Here we are making `HTTP PUT` request using `hx-put="/todo/<%= todo.id %>"` and update the information about the item. I did not include the Completion here, as there's a separate function for that.
541+
542+
I had a problem here when I used the `id` attribute of the HTML elements. When sending a HTML form back (using in our case `hx-put`) we need to use the `name`-attributes on `<input>` elements. `id` are for using the JavaScript DOM.
543+
507544
## Summary
508545

509546
That's it for this post. We have built a fully-fledge (albeit missing some error handling and validation, I'm happy to admit) todo application, storing the information in a collection per logged in user.

_posts/2025-01-17-htmx-todo-tutorial-IV.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,9 @@ Let's take the `router.delete("/:id")` as an example. Add the `HX-Trigger` heade
142142
```javascript
143143
router.delete("/:id", async (req, res) => {
144144
await deleteTodo(req.params.id);
145-
res
146-
.status(204) // no content
147-
.header("HX-Trigger", '{"ITEM_UPDATED": `The ${id} item was DELETED`}')
148-
.send();
145+
146+
res.setHeader("HX-Trigger", '{"ITEM_UPDATED": `The ${id} item was DELETED`}');
147+
res.send();
149148
});
150149
```
151150

@@ -194,6 +193,11 @@ There's only one problem, it's not showing up. That's because our `counter.ejs`
194193
And then, only for the `/` route I can pass `addSwapOOB:true`:
195194

196195
```javascript
196+
router.get("/", async (req, res) => {
197+
const todos = await getAllTodos();
198+
const counters = getCounters(todos);
199+
res.render("todo/list.ejs", { todos, counters, addSwapOOB: true });
200+
});
197201
```
198202

199203
I'm not too happy but it will help to show the different approaches.
@@ -216,7 +220,7 @@ router.put("/:id/toggle", async (req, res) => {
216220
});
217221
```
218222

219-
And now the counters are updated through the events that gets triggered
223+
And now the counters are updated through the events that gets triggered. See the [end result here](https://github.com/marcusoftnet/htmx-todo-tutorial/blob/main/routes/todo.js).
220224

221225
## Summary
222226

@@ -238,4 +242,4 @@ I learned a lot by writing this series and I hope you found it useful too.
238242

239243
I think HTMx is a breath of fresh air for web developers that, like me, have got lost in SPA frameworks and JSON-to-HTML parsing. It brings the pure ideas of the web back to the forefront while still allows me to write websites that only rerenders the part of the application that has changed.
240244

241-
[The code is found here in the state that I left it in at the end of this post.](https://github.com/marcusoftnet/htmx-todo-tutorial/tree/406bda133d83410d85c52286f66a4f0124b19e6e)
245+
[The code is found here in the state that I left it in at the end of this post.](https://github.com/marcusoftnet/htmx-todo-tutorial/tree/406bda133d83410d85c52286f66a4f0124b19e6e). I then did some additional refactorings and the [`main` branch contains these improvements.](https://github.com/marcusoftnet/htmx-todo-tutorial)

0 commit comments

Comments
 (0)