Single state struct for a MishkaGervaz form LiveView.
All per-request form state lives on t/0. Instead of scattering values
across LiveView assigns, every consumer of the form pipeline reads from
and writes to this struct, giving:
- One clearly-typed shape (
t/0andt:Static.t/0). - One place to thread updates (
update/2). - One source of truth for events, the renderer, and tests.
Performance split
State is partitioned into two halves:
static(t:Static.t/0) — configuration that never changes afterinit/3. Same struct reference for the lifetime of the form, which lets LiveView skip re-rendering nodes that depend only on it.- Dynamic fields — user-interaction state (form values, current step, relation options, errors, …) that drive re-renders.
Sub-builders
init/3 composes its work from five overridable sub-builder modules.
The DSL (state do … end) can override any of them per-resource, and
the use macro accepts the same set as compile-time options.
MishkaGervaz.Form.Web.State.FieldBuilder— resolved field configs.MishkaGervaz.Form.Web.State.GroupBuilder— group layout.MishkaGervaz.Form.Web.State.StepBuilder— wizard / tabs step plan.MishkaGervaz.Form.Web.State.Presentation— UI adapter, template, theme, features, debounce.MishkaGervaz.Form.Web.State.Access— master gate, action mapping, preload selection.
Override patterns
Override the entire state module:
defmodule MyApp.Form.State do
use MishkaGervaz.Form.Web.State
def init(id, resource, user) do
state = super(id, resource, user)
MishkaGervaz.Form.Web.State.update(state, mode: :update)
end
endUse update/2 (or struct/2) to mutate fields — the struct shape is
fixed and there is no :custom_field. To carry your own data, attach
it to state.static.config (read-only, set at build time) or stage it
on state.field_values.
Override specific sub-builders:
defmodule MyApp.Form.State do
use MishkaGervaz.Form.Web.State,
field: MyApp.Form.FieldBuilder,
group: MyApp.Form.GroupBuilder
endOr override via DSL:
mishka_gervaz do
form do
state do
field MyApp.Form.FieldBuilder
group MyApp.Form.GroupBuilder
end
end
endOverride entire state module via DSL:
mishka_gervaz do
form do
state module: MyApp.Form.CustomState
end
endSee MishkaGervaz.Form.Web.State.Helpers (shared utilities exposed to
the macro and to user overrides), MishkaGervaz.Form.Web.Live,
MishkaGervaz.Form.Web.Events, MishkaGervaz.Form.Web.DataLoader,
MishkaGervaz.Form.Behaviours.Template, and the table-side counterpart
MishkaGervaz.Table.Web.State.
Summary
Functions
See MishkaGervaz.Form.Web.State.Default.current_step_fields/1.
See MishkaGervaz.Form.Web.State.Default.current_step_groups/1.
See MishkaGervaz.Form.Web.State.Default.default_init/3.
See MishkaGervaz.Form.Web.State.Default.get_action/2.
See MishkaGervaz.Form.Web.State.Default.get_preloads/1.
See MishkaGervaz.Form.Web.State.Default.init/3.
See MishkaGervaz.Form.Web.State.Default.multi_step?/1.
See MishkaGervaz.Form.Web.State.Default.tabs_mode?/1.
See MishkaGervaz.Form.Web.State.Default.update/2.
See MishkaGervaz.Form.Web.State.Default.wizard_mode?/1.
Types
@type form_mode() :: :create | :update
@type loading_status() :: :initial | :loading | :loaded | :error | :denied
@type t() :: %MishkaGervaz.Form.Web.State{ combobox_options: %{required(atom()) => [{String.t(), String.t()}]}, current_step: atom() | nil, current_user: map() | nil, defaults: map() | nil, dirty?: boolean(), dismissed_notices: MapSet.t(), errors: map(), existing_files: %{required(atom()) => [map()]}, field_values: map(), form: Phoenix.HTML.Form.t() | nil, form_errors: [String.t()], loading: loading_status(), master_user?: boolean(), mode: form_mode(), preload_aliases: %{required(atom()) => atom()}, relation_options: map(), static: MishkaGervaz.Form.Web.State.Static.t(), step_states: %{required(atom()) => :pending | :active | :completed | :error}, upload_state: map(), wizard_history: [atom()] }
Functions
See MishkaGervaz.Form.Web.State.Default.current_step_fields/1.
See MishkaGervaz.Form.Web.State.Default.current_step_groups/1.
See MishkaGervaz.Form.Web.State.Default.default_init/3.
See MishkaGervaz.Form.Web.State.Default.get_action/2.
See MishkaGervaz.Form.Web.State.Default.get_preloads/1.
See MishkaGervaz.Form.Web.State.Default.init/3.
See MishkaGervaz.Form.Web.State.Default.multi_step?/1.
See MishkaGervaz.Form.Web.State.Default.tabs_mode?/1.
See MishkaGervaz.Form.Web.State.Default.update/2.
See MishkaGervaz.Form.Web.State.Default.wizard_mode?/1.