Events
Elements in a spec can define event handlers via the on property. When a rendered component calls its emit function, the library looks up the corresponding action binding and dispatches it to a registered handler.
#How Events Work
The event flow has three parts:
- Element definition -- the
onproperty maps event names to action bindings - Component -- calls
emit('eventName')when the user interacts - Handler -- a registered function that executes the action
#Defining Event Handlers in a Spec
The on property on a UIElement maps event names to action bindings:
Each binding has:
| Property | Type | Description |
|---|---|---|
action | string | The key used to look up the handler function |
params | Record<string, unknown> | Parameters passed to the handler |
#Multiple Handlers per Event
An event can trigger multiple actions by using an array:
Both handlers are called in order when the component emits click.
#The Emit Function
Every rendered component receives an emit input -- a function with the signature (event: string) => void. Call it from your component to dispatch an event:
Because emit is declared with input(), it is a Signal. Call this.emit() to get the function, then invoke it with the event name: this.emit()('click').
#Registering Handlers
Handlers are plain functions registered either globally via provideRender() or per-instance on <render-spec>:
#Handler Signature
Each handler receives a params object and can return a value or a Promise:
#Injection Context
Handlers execute inside Angular's runInInjectionContext. This means you can call inject() to access services:
This works for handlers passed via [handlers] on <render-spec>, provideRender(), or other render-enabled components like ChatComponent (from @threadplane/chat).
#Resolution Priority
Handlers resolve with the same priority as other inputs:
handlersinput on<render-spec>(highest priority)handlersinprovideRender()config (fallback)
#Action Dispatch Pattern
A common pattern is to use handlers to update the state store in response to user interactions. That creates a unidirectional data flow:
Here's a complete example:
#Async Handlers
Handlers can be asynchronous. The library does not await the return value, but you can use async functions for server calls or other asynchronous operations:
#Observing Render Events
Beyond dispatching handlers, <render-spec> emits a single stream of every notable thing that happens during rendering through its events output. Bind to it to observe handler dispatch, state changes, and mount/destroy lifecycle in one place:
RenderEvent is a discriminated union of three variants, keyed by type:
type | Interface | Fires when | Notable fields |
|---|---|---|---|
'handler' | RenderHandlerEvent | A handler finishes running | action, params, result? |
'stateChange' | RenderStateChangeEvent | The store value changes | path, value, snapshot |
'lifecycle' | RenderLifecycleEvent | A spec or element mounts/destroys | event ('mounted' | 'destroyed'), scope ('spec' | 'element'), elementKey?, elementType? |
All three interfaces are exported from @threadplane/render. This output is the single source the Lifecycle guide builds its RENDER_LIFECYCLE signals on top of -- both observe the same stream, so there's no double-counting.