Calmcode - htmx: todo. part 2

Todo App, Part 2

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>

The main thing to observe at the end here is that the hx-trigger="load" attribute is used to fetch all the todos sofar. This is a neat pattern, because it again moves all the required logic to the backend.