Current section

Files

Jump to
dala lib dala event component.ex
Raw

lib/dala/event/component.ex

defmodule Dala.Event.Component do
@moduledoc """
Behaviour for **stateful** event-owning components.
See `guides/event_model.md` for the model. In short:
- **Stateless** components are plain functions: `(assigns) -> render_tree`.
They have no event horizon — events fired inside their subtree resolve to
the nearest stateful ancestor.
- **Stateful** components implement `Dala.Event.Component`. They own events
fired in their subtree and may escalate semantic events to their parent.
This is the Dala equivalent of `Phoenix.LiveComponent`.
## Status
This is the **interface declaration**. The runtime that hosts these
components — registering them in render trees, routing events to them,
managing their lifecycle — is implemented incrementally:
1. Existing `Dala.Component` (a sibling concept for native_view widgets)
remains the runtime for components that pair with custom native views.
2. `Dala.Event.Component` (this module) is the *event-routing* abstraction:
a stateful owner of events for a subtree of standard widgets.
3. The two will likely merge once the new event model is plumbed end-to-end.
Until then, the Bridge module handles the legacy event shapes and the
existing `Dala.Component` is the canonical stateful component.
## Callbacks
defmodule MyApp.CheckoutForm do
use Dala.Event.Component
def mount(props, state), do: {:ok, Map.put(state, :email, "")}
def render(state) do
# return a render tree (uses Dala.UI helpers)
%{type: :column, ...}
end
def handle_event(%Address{id: :email}, :change, value, state) do
{:noreply, %{state | email: value}}
end
def handle_event(%Address{id: :submit}, :tap, _, state) do
# Escalate to parent — this is the "semantic" event.
send(state.parent_pid, {:form_submitted, state.email})
{:noreply, state}
end
end
"""
alias Dala.Event.Address
@callback mount(props :: map(), state :: map()) ::
{:ok, map()} | {:error, term()}
@callback update(props :: map(), state :: map()) ::
{:ok, map()}
@callback render(state :: map()) :: map()
@callback handle_event(
addr :: Address.t(),
event :: atom(),
payload :: term(),
state :: map()
) :: {:noreply, map()}
@callback handle_info(message :: term(), state :: map()) ::
{:noreply, map()}
@callback terminate(reason :: term(), state :: map()) :: term()
@optional_callbacks [update: 2, handle_info: 2, terminate: 2]
defmacro __using__(_opts) do
quote do
@behaviour Dala.Event.Component
alias Dala.Event.Address
def mount(_props, state), do: {:ok, state}
def update(props, state), do: mount(props, state)
def handle_event(_addr, _event, _payload, state), do: {:noreply, state}
def handle_info(_message, state), do: {:noreply, state}
def terminate(_reason, _state), do: :ok
defoverridable mount: 2,
update: 2,
handle_event: 4,
handle_info: 2,
terminate: 2
end
end
end