Skip to content

3. Shareable filters

Goal: all / open / closed tabs whose state survives a reload and travels in a link.

Declare a URL signal

components/tracker.py (the new line)
from situ import Local, Server, Url

search: Local[str] = ""
filter: Url[str] = "all"      # all | open | closed — lives in the query string
issues: Server[object]

and add the tabs at the top of the region:

components/tracker.html (inside <div data-region>, above the list)
<nav>
  <a href="?filter=all" class="{{ 'selected' if filter == 'all' else '' }}">all</a>
  <a href="?filter=open" class="{{ 'selected' if filter == 'open' else '' }}">open</a>
  <a href="?filter=closed" class="{{ 'selected' if filter == 'closed' else '' }}">closed</a>
  <span class="count">{{ issues | length }} shown</span>
</nav>

(The count moved into the <nav>; drop the old <p class="count"> line.)

A Url signal has no storage of its own — the query string is the store. You set it with a link; the server reads it back: context()'s view parameter (wired in part 1) carries the current URL signals with their declared defaults, so service.visible(which) already filters and the selected class renders server-side. There was nothing else to build.

Restart and click closed: the URL becomes /?filter=closed, the page navigates, the server renders the filtered list. Copy the URL into another tab — same view. Bookmark it, share it; that is the point of the site.

The trade-off, on purpose

Type something into the search box, then click a filter tab. The search box clears.

A Url change is a real navigation — the page reloads, and in-progress Local state (your search text) resets to its defaults. That is the deal this site offers: link semantics in exchange for page lifetime. Both behaviors are correct for different state — which is exactly why situ puts the choice on each signal, while most stacks decide it once for the whole app. Had you wanted the filter to keep the search text alive, filter could be Local; it would then stop being shareable.

One more property you get for free: when commands arrive in part 5, the current URL signals ride along on every POST automatically, so the server re-renders the region for the filter you are looking at.

Next: Part 4 — selection and a detail pane.