Summary#
on change { … } runs a block reactively — the runtime re-runs it whenever a value it reads changes. Use it to
push a value from the component to somewhere outside it. The canonical case is a page naming its own route:
component OrgEdit(string slug) {
var org = Organization.Single(o => o.Slug == slug);
on change { Navigation.SetTitle(org.Name); } // the tab + browser title track the org's name
}It belongs to the on <event> lifecycle-event family — on mount (setup, once), on change (a tracked reaction),
on unmount (teardown, once). See on mount / on unmount for the once-only siblings and The reactivity & lifecycle model for the whole
execution model.
Signature#
on change { <statements> } // re-runs whenever a reactive value it read changeson change takes no name — the on <event> family is anonymous. (Don't confuse it with an onChange input
prop: Input(value: x, onChange: Handler) is a field's change event, a different thing.)
Description#
What it's for#
An on change block is for outward work — a call whose result lands somewhere the component doesn't own:
| You want | Use |
|---|---|
| A computed value to render | live var name = expr; |
| Setup that runs once, when the page opens | on mount { … } (on mount / on unmount) |
| Teardown that runs once, when the page closes | on unmount { … } (on mount / on unmount) |
| To keep something outside the component in sync as data changes | on change { … } |
An on change body has the same powers as an action — it can call server functions and reach the client verbs
(Navigation.*, Theme.*). The runtime calls it for you instead of a click.
It may not write its own state#
Assigning the component's own field from an on change block is a compile error:
an
on changeblock must not assign the component's own reactive state (count). That would loop: the write wakes the reaction, which re-runs it.
The rule holds even if the write hides behind an action or method the block calls — the compiler follows the call. If
you need a value, use a live var computed; if you need to set state once, use on mount; if a user gesture should set
it, use an action.
When it runs#
It runs at mount (to establish its dependencies and do the initial sync), then again whenever any reactive value
it read changes — and only then. It is dependency-tracked: reading org.Name subscribes the block to org.Name,
so an unrelated change elsewhere on the page does not wake it. This is the whole point of the name — "on change" is
tracked-by-construction, where a block "that runs every render" would be waste. Triggers are any reactive read, not
only a live var: a plain state field reassigned by an action wakes it too. See The reactivity & lifecycle model.
Writing a body that is idempotent — safe to run again with the same inputs — is the contract; the platform
de-duplicates at the sink where it can (calling Navigation.SetTitle with an unchanged title does nothing).
Examples#
A create page whose title tracks the name as you type it — and falls back while the name is still blank:
entity Organization { string Name; }
[Page("/org/new")]
[Title("New organization")] // the static fallback (server-rendered, and before the reaction first runs)
[Render(CSR)]
component OrgCreate() {
Organization draft;
on mount { draft = new Organization {}; }
live var tabName = draft?.Name ?? "New organization";
on change { Navigation.SetTitle(tabName); } // the tab + browser title update as you type
render {
Stack(gap: 4) { Input(value: draft.Name, placeholder: "Organization name"); }
}
}See also#
- component — the component
on changelives in, and its other members. - on mount / on unmount —
on mount/on unmount, for setup and teardown that run once rather than reactively. - The reactivity & lifecycle model — the execution model: declarations vs
on mountvson changevson unmount, and how a change updates only the slots that read it. - Navigation —
Navigation.SetTitle, and the rest of the route surface anon changeblock can reach.