Calmcode - htmx: todo. part 1

Todo App, Part 1

1 2 3 4 5 6 7

In this video we will assume this Flask app:

import uuid

from flask import Flask, render_template, request

app = Flask(__name__)

todos = []


@app.route("/")
def index():
    return render_template("04-index.html", todos=render_all_todos())


@app.route("/todos")
def render_all_todos():
    return "".join([render_todo_item(todo["id"], todo["content"]) for todo in todos])


def render_todo_item(todo_id, todo_content):
    return f"""<div id="{ todo_id }">
        <span>{ todo_content }</span>
        <button hx-post="/delete/{ todo_id }"
                hx-target="#{ todo_id }"
                hx-swap="outerHTML">
            Delete
        </button>
    </div>
    """


@app.route("/add", methods=["POST"])
def add():
    global todos
    content = request.form["content"]
    todo = {"id": "uniq" + str(uuid.uuid4())[:10], "content": content}
    todos.append(todo)
    return render_todo_item(todo["id"], todo["content"]), 201


@app.route("/delete/<id>", methods=["POST"])
def delete(id):
    global todos
    todos = [todo for todo in todos if todo["id"] != id]
    return "", 200


if __name__ == "__main__":
    app.run(debug=True)

This app assumes that there is a folder called templates with an 03-index.html file that looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Simple HTMX Todo App</title>
    <script src="https://unpkg.com/htmx.org"></script>
</head>
<body>
    <h1>Simple HTMX Todo App</h1>
    <form hx-post="/add" hx-target="#todo-list" hx-swap="beforeend">
        <input type="text" name="content" 
               placeholder="Add a new todo" id="text-input" required>
        <button type="submit">Add</button>
    </form>
    <div id="todo-list" 
         hx-trigger="load" 
         hx-get="/todos" 
         hx-swap="innerHTML">
    </div>
</body>
</html>

Notice how the backend contains a lot of logic now, but that the code for the frontend is relatively modest? This is not unusual when you are writing code with HTMX.

The main thing to observe here is how the HTML code that we return can itself also contain HTMX attributes. This makes it easy to implement the deletion behavior for each individual element. When that happens we need to sync with the backend, but the UI for this is relatively straightforward to implement because the logic of this all takes place on the backend.