GitHub

Adding Filters (All • Active • Completed)

Goal: Finish our TodoMVC by letting users slice the list between All, Active, and Completed items. You'll declare one more query‑parameter, adjust the from query, and add a footer navigation that highlights the selected filter.

Estimated time: 10 minutes.

« Part 4: Deleting & Bulk Actions

1 New concepts

ConceptPurpose
param … pattern="…" defaultDeclare and validate a query parameter (filter) in GET requests.
Context‑aware ifDynamically add the selected CSS class to the active link.
SQL condition using bound paramsShow only the rows that match the chosen filter.

2 Declare the filter parameter

Near the top of templates/todos.pageql, right after any existing let or create tags, insert:


{{param filter default='all' pattern="^(all|active|completed)$" optional}}
{%param filter default='all' pattern="^(all|active|completed)$" optional%}

optional means the page also works without the query string.

3 Update the from loop

Replace the simple query with a conditional one:


{%from todos
  WHERE (:filter == 'all')
        OR (:filter == 'active'     AND completed = 0)
        OR (:filter == 'completed'  AND completed = 1)
  ORDER BY id%}
  … list‑item markup …
{%end from%}
{%from todos WHERE (:filter == 'all') OR (:filter == 'active' AND completed = 0) OR (:filter == 'completed' AND completed = 1) ORDER BY id%} … list‑item markup … {%end from%}

The entire boolean expression is sent to SQLite; :filter is safely bound.

4 Add the filter links in the footer

Inside the <footer class="footer"> block (just after the item‑count), add:


<div class="filters">
  <a {%if :filter == 'all'%}class="selected"{%end if%} href="/todos?filter=all">All</a> |
  <a {%if :filter == 'active'%}class="selected"{%end if%} href="/todos?filter=active">Active</a> |
  <a {%if :filter == 'completed'%}class="selected"{%end if%} href="/todos?filter=completed">Completed</a>
</div>
<div class="filters"> <a {%if :filter == 'all'%}class="selected"{%end if%} href="/todos?filter=all">All</a> | <a {%if :filter == 'active'%}class="selected"{%end if%} href="/todos?filter=active">Active</a> | <a {%if :filter == 'completed'%}class="selected"{%end if%} href="/todos?filter=completed">Completed</a> </div>

The CSS from TodoMVC highlights the link that carries class="selected".

5 Try it out

  1. Open http://localhost:8000/todosAll is selected by default.
  2. Click Active — only incomplete items remain.
  3. Tick a few checkboxes so they disappear from the Active view, then click Completed to confirm they moved.
  4. Manually tamper with the URL (e.g. ?filter=hack) → PageQL blocks the request because it fails the regex pattern.

6 How it works

6 Wrap‑up

Congratulations 🎉 You have:

PageQL let us ship a reactive, database‑driven TodoMVC in ≈120 lines of HTML+SQL. From here you can explore:

Enjoy building with PageQL, and thanks for following the tutorial! 🎊

« Back: Part 4: Deleting & Bulk Clear Next: Part 6: Integration & Extensibility »