Server actions
The server side of actions: when an interaction changes the truth, `on_<event>=` receives a Python function — a handler. One round trip, and the truth mutates server side.Wiring a handler
Every interactive component exposes events (`click`, `change`, `input`, `focus`, `blur`, `submit`, …). One wires a function with `on_<event>=`.def save() -> None:
...
ui.button("Save", on_click=save)
What can be passed to on_<event>=
The handler is found again by its import path (`module::function`), with no per-page table stored. So it must be addressable.| Form | Accepted? |
|---|---|
| Module-level function (on_click=save) | Yes |
| @staticmethod / @classmethod | Yes |
| functools.partial(handler, arg) | Yes — to pass an argument |
| Lambda (on_click=lambda: …) | No — error at render |
| A closure (a function defined inside a function) | No — error at render |
| Instance method | No — not addressable |
This is the server route (a function). `on_<event>=` also accepts a string, evaluated client side with no handler — cf.Client actions.
Passing an argument — functools.partial
To give the handler an argument (typically a row's id), one uses `partial`. The arguments travel with the request.from functools import partial
def delete_item(item_id: str) -> None:
...
# one row per item, each with its own id:
ui.icon_button("trash-2",
on_click=partial(delete_item, item_id))
The arguments must be JSON-serialisable: str, int, float, bool, None, list, dict.Reading the form's data
Three ways, depending on the need:from bretzel.state import get
# 1. a parameter named like a field → forwarded
def submit(title: str) -> None:
...
# 2. a State-typed parameter → hydrated from the form
def save(form: Draft) -> None:
# form.title, form.price … already filled + validated
...
# 3. get() → one raw field, occasionally
def other() -> None:
note = get("note")
No `name=` to write by hand: `ui.input(value=draft.title)` derives the `title` field automatically (cf. the Components chapter).Avoiding double submits — @idempotent
On a sensitive action (payment, creation), `@idempotent` makes a double send of the same render run only once; the second receives a 204.from bretzel import idempotent
@idempotent
def charge_card() -> None:
...
What next
What re-renders after the handler:Server reactivity →
Acting with no round trip:Client actions →