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.