1. A server-rendered list¶
Goal: rows from the store on the screen, through the full mount — the skeleton every later part hangs off.
The mount¶
from __future__ import annotations
from collections.abc import Mapping
from pathlib import Path
import situ
from dishka import Provider, Scope, make_async_container, provide
from dishka.integrations.litestar import setup_dishka
from litestar import Litestar
from litestar.plugins.jinja import JinjaTemplateEngine
from litestar.static_files import create_static_files_router
from litestar.template.config import TemplateConfig
from situ import mount_component
from store import IssueStore
HERE = Path(__file__).parent
class AppProvider(Provider):
scope = Scope.APP
@provide
def store(self) -> IssueStore:
return IssueStore() # one in-memory store shared by every request
async def context(service: IssueStore, view: Mapping[str, str]) -> Mapping[str, object]:
"""(service, url-view) -> the dict the template renders, fresh on every render."""
which = view.get("filter", "all")
return {"issues": service.visible(which), "filter": which}
def create_app() -> Litestar:
app = Litestar(
route_handlers=[
mount_component(
path="/",
stem=HERE / "components" / "tracker",
template="page.html",
meta={"name": "Tracker"},
service_type=IssueStore,
context=context,
),
create_static_files_router(path="/static", directories=[situ.static_dir()]),
],
template_config=TemplateConfig(
directory=[HERE / "templates", situ.templates_dir()],
engine=JinjaTemplateEngine,
),
)
setup_dishka(make_async_container(AppProvider()), app)
return app
app = create_app()
Three things to register:
mount_componentcompiles the component pair once and derives every route.service_type=IssueStoreis what gets resolved from the Dishka container per request;contextturns the service (plus the URL view — used from part 3) into the dict the template renders.- The template config lists two directories — yours first, then situ's — so
template="page.html"resolves your copy while situ's default stays available as a fallback. - The static router serves
situ.static_dir(), where the shared shim_rt.jslives.
The component, v1¶
One signal, sited Server: the authoritative value lives on the server. In the template, issues is whatever context() returned under that name — ordinary Jinja renders it. (The object type parameter becomes a typed facade Protocol in part 5, when handlers start calling it.)
The <div data-region> matters: it is one of the two roots situ wires at boot, and the only part of the page a server command will re-render later. Keep data-region as the div's first attribute.
Run it¶
Three rows render. --reload restarts the process on every file save, which matters here: a mount compiles its component once per process, so the restart is what picks up your edits throughout this tutorial.
What got derived¶
Two routes exist so far — GET / (the page) and GET /island.js (the generated island, ~5.3 KB even now: it carries the shared boot scaffolding, waiting for signals). No command routes yet: the component has no handlers. Open /island.js and read it; it will grow with each part.
Next: Part 2 — live search, where the browser gets its first signal.