Packages
mob
0.6.14
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
lib/mob/event/component.ex
defmodule Mob.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 `Mob.Event.Component`. They own events
fired in their subtree and may escalate semantic events to their parent.
This is the Mob 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 `Mob.Component` (a sibling concept for native_view widgets)
remains the runtime for components that pair with custom native views.
2. `Mob.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 `Mob.Component` is the canonical stateful component.
## Callbacks
defmodule MyApp.CheckoutForm do
use Mob.Event.Component
def mount(props, state), do: {:ok, Map.put(state, :email, "")}
def render(state) do
# return a render tree (uses Mob.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 Mob.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 Mob.Event.Component
alias Mob.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