Deleting & Bulk Actions (reactive)
Goal: Finish the CRUD circle—remove single Todos and clear all completed items. You'll meet #delete
, revisit multi‑row #update
, and learn how PageQL wraps all mutations of one request in a single transaction. The browser reflects changes instantly via hx-*
actions.
Estimated time: 10 minutes.
1 New concepts
Concept | Purpose |
---|---|
#delete | Permanently removes rows that match a WHERE clause. |
Bulk write queries | #update or #delete without WHERE id = … affect many rows at once. |
Implicit transaction | PageQL opens a database transaction at the first data‑modifying tag and commits on success; if any tag fails, everything rolls back automatically. |
2 Make "delete" reachable in the UI
2.1 Destroy button inside each list item
Add the italicised hx-*
attributes to the view version of the <li>
(the one rendered when not editing):
<li {{#if completed}}class="completed"{{/if}}> <input hx-post="/todos/{{id}}/toggle" class="toggle" type="checkbox" {{#if completed}}checked{{/if}}> <label hx-get="/?edit_id={{id}}">{{text}}</label> <button hx-delete="/todos/{{id}}" class="destroy" style="cursor:pointer; background:none; border:none; color:#ac4a1a;">✕</button> </li>
<li {{#if completed}}class="completed"{{/if}}> <input hx-post="/todos/{{id}}/toggle" class="toggle" type="checkbox" {{#if completed}}checked{{/if}}> <label hx-get="/?edit_id={{id}}">{{text}}</label> <button hx-delete="/todos/{{id}}" class="destroy" style="cursor:pointer; background:none; border:none; color:#ac4a1a;">✕</button> </li>
(The destroy
class is defined by TodoMVC CSS and draws an × icon.)
2.2 Clear‑completed button in the footer
Replace or add this inside the existing <footer class="footer">
block after the filter links:
{{#if :completed_count > 0}} <button class="clear-completed" hx-post="/todos/clear_completed">Clear completed</button> {{/if}}
{{#if :completed_count > 0}} <button class="clear-completed" hx-post="/todos/clear_completed">Clear completed</button> {{/if}}
3 New public partials
Append to the bottom of your actions file (or inline):
{{#partial delete :id}} {{#delete from todos WHERE id = :id}} {{/partial}} {{#partial post clear_completed}} {{#delete from todos WHERE completed = 1}} {{/partial}}
{{#partial delete :id}} {{#delete from todos WHERE id = :id}} {{/partial}} {{#partial post clear_completed}} {{#delete from todos WHERE completed = 1}} {{/partial}}
Notice: there is no explicit BEGIN
/COMMIT
. If an error occurs during the partial, PageQL aborts the whole request and rolls back the delete.
This highlights one of PageQL's key features - all data-modifying operations within a single request are automatically wrapped in a transaction. This ensures that your database remains in a consistent state even if an error occurs during processing.
4 Try it out
- Complete a couple of todos, then click Clear completed—the list updates instantly showing only active items.
- Click the × next to a todo—item vanishes.
- Deliberately break the SQL inside
clear_completed
(e.g., change the table name totodoz
) and test again ➜ all rows survive; the error page shows the failed query, proving rollback.
5 Under the hood: request transaction timeline
┌─ HTTP POST /todos/clear_completed ─┐
│ BEGIN TRANSACTION │ (implicit)
│ DELETE FROM todos WHERE … │ ← #delete
│ COMMIT │ (on success)
└─ UI updates automatically ────────┘
Multiple #insert
, #update
, and #delete
tags in one partial still share one transaction boundary—handy for complex wizard‑style forms.
This automatic transaction handling is particularly valuable for operations that need to maintain data integrity, such as forms that update multiple tables at once or operations that have multiple steps that must either all succeed or all fail.
6 Recap & next step
#delete
rounds out CRUD; filter counts already react thanks to the#let
variables from Part 3.- PageQL handles error‑safe transactions for the common case—no ceremony required.
- You now have a fully working TodoMVC clone using only HTML ± SQL.