Single state struct for a MishkaGervaz form LiveView.
All per-request form state lives on t/0. Every consumer of the form
pipeline — events, the renderer, tests — reads from this struct and
writes to it through update/2.
Starting over
opened counts the times the form has started over: opened again with reset, moved to another
record, or returned to an empty form by a save. A live component drawn inside the form that keeps
state of its own compares it with the count it last saw, and starts over with the form.
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
Applies a mount's own presentation choices over the resource's — see the __using__ version.
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(), opened: non_neg_integer(), pickers: %{required(atom()) => Date.t()}, 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
Applies a mount's own presentation choices over the resource's — see the __using__ version.
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.