Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol examples getting_started form.ex
Raw

examples/getting_started/form.ex

defmodule Raxol.Examples.FormDemo do
@moduledoc """
A sample form component that demonstrates parent-child interactions.
This component includes:
- Child component management
- Event bubbling
- State synchronization
- Error boundaries
"""
use Raxol.Core.Runtime.Application
require Raxol.Core.Runtime.Log
require Raxol.View.Elements
alias Raxol.View.Elements, as: UI
defstruct form_data: %{username: "", password: ""},
submitted: false,
id: :form_example
@doc """
Initialize the form component with default state (for testing compatibility).
"""
def new(props \\ %{}) do
%{
submitted: false,
button_clicked: nil,
fields: Map.get(props, :fields, %{}),
on_submit: Map.get(props, :on_submit, fn -> :submitted end),
children: Map.get(props, :children, []),
errors: Map.get(props, :errors, %{}),
id: Map.get(props, :id, :form_example),
form_data: Map.get(props, :form_data, %{username: "", password: ""})
}
end
@impl Raxol.Core.Runtime.Application
def init(_opts) do
{:ok, %__MODULE__{}}
end
@impl Raxol.Core.Runtime.Application
def update({:input, field, value}, state) do
new_form_data = Map.put(state.form_data, field, value)
{%{state | form_data: new_form_data}, []}
end
def update({:submit}, state) do
# Log submission, don't emit command
Raxol.Core.Runtime.Log.info("Form submitted: #{inspect(state.form_data)}")
{%{state | submitted: true}, []}
end
def update(_msg, state) do
# Ignore other messages
{state, []}
end
# Correct arity for Application behaviour
@impl Raxol.Core.Runtime.Application
def handle_event(
{:raxol_event, _source_pid,
{:component_event, component_id, event_type, payload}}
) do
# Translate component events into update messages
case {component_id, event_type} do
# Assuming payload is the new value
{:username_input, :change} -> [{:input, :username, payload}]
# Assuming payload is the new value
{:password_input, :change} -> [{:input, :password, payload}]
# Assuming any click in the form context is a submit
{_, :click} -> [:submit]
# Ignore other component events
_ -> []
end
end
def handle_event(_event) do
# State is managed by Dispatcher, just return commands/messages
# Keep this catch-all for other event types (e.g., keyboard, mouse if needed)
[]
end
@impl Raxol.Core.Runtime.Application
# Keep state for handle_message
def handle_message(_msg, state), do: {state, []}
# Correct arity for Application behaviour
@impl Raxol.Core.Runtime.Application
def handle_tick(_tick) do
# State is managed by Dispatcher, just return commands
[]
end
@impl Raxol.Core.Runtime.Application
def subscriptions(_state), do: []
@impl Raxol.Core.Runtime.Application
def terminate(_reason, _state), do: :ok
@doc """
Render the form UI.
"""
@impl Raxol.Core.Runtime.Application
@dialyzer {:nowarn_function, view: 1}
def view(state) do
UI.box id: state.id, border: :rounded, padding: 1 do
UI.column do
[
UI.label("Username:"),
UI.text_input(id: :username_input, value: state.form_data.username),
UI.label("Password:"),
UI.text_input(
id: :password_input,
value: state.form_data.password,
password: true
),
UI.button(id: :submit_button, label: "Submit"),
case state.submitted do
true -> UI.label("Submitted!")
false -> nil
end
]
|> Enum.reject(&is_nil/1)
end
end
end
# Add a render/2 function for test compatibility
# def render(state, _context) do
# view(state)
# end
# Add handle_event/3 for integration testing with Raxol.Test.Integration
# This will be called by the test framework when events bubble from children.
def handle_event(
state,
%Raxol.Core.Events.Event{type: :button_pressed},
_opts
) do
# Log submission (similar to update/2 logic)
Raxol.Core.Runtime.Log.info(
"Form received :button_pressed, processing as submit: #{inspect(state.form_data)}"
)
new_state = %{state | submitted: true}
# For assert_parent_updated to pass, parent must dispatch an event of type :button_clicked.
# Use a new :dispatch_to_self command that the integration framework will handle.
{:update, new_state,
[{:dispatch_to_self, %Raxol.Core.Events.Event{type: :button_clicked}}]}
end
# Catch-all for other events in integration test context if needed,
# ensuring it doesn't conflict with the Application behaviour handle_event/1.
# This specific arity is for the test framework.
def handle_event(state, _event, _opts) do
# Default pass-through for other events not specifically handled by Form in this context
# Or simply :passthrough if state is guaranteed to be a component state map
{:noreply, state, []}
end
end