MishkaGervaz.Form.Web.State (MishkaGervaz v0.0.1-alpha.2)

Copy Markdown View Source

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/0 and t: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 after init/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.

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
end

Use 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
end

Or override via DSL:

mishka_gervaz do
  form do
    state do
      field MyApp.Form.FieldBuilder
      group MyApp.Form.GroupBuilder
    end
  end
end

Override entire state module via DSL:

mishka_gervaz do
  form do
    state module: MyApp.Form.CustomState
  end
end

See 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

form_mode()

@type form_mode() :: :create | :update

loading_status()

@type loading_status() :: :initial | :loading | :loaded | :error | :denied

t()

@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

current_step_fields(state)

@spec current_step_fields(t()) :: [map()]

See MishkaGervaz.Form.Web.State.Default.current_step_fields/1.

current_step_groups(state)

@spec current_step_groups(t()) :: [map()]

See MishkaGervaz.Form.Web.State.Default.current_step_groups/1.

default_init(id, resource, current_user)

@spec default_init(String.t(), module(), map() | nil) :: t()

See MishkaGervaz.Form.Web.State.Default.default_init/3.

get_action(state, action_type)

@spec get_action(t(), atom()) :: atom()

See MishkaGervaz.Form.Web.State.Default.get_action/2.

get_preloads(state)

@spec get_preloads(t()) :: [atom()]

See MishkaGervaz.Form.Web.State.Default.get_preloads/1.

init(id, resource, current_user)

@spec init(String.t(), module(), map() | nil) :: t()

See MishkaGervaz.Form.Web.State.Default.init/3.

multi_step?(state)

@spec multi_step?(t()) :: boolean()

See MishkaGervaz.Form.Web.State.Default.multi_step?/1.

tabs_mode?(state)

@spec tabs_mode?(t()) :: boolean()

See MishkaGervaz.Form.Web.State.Default.tabs_mode?/1.

update(state, updates)

@spec update(t(), keyword() | map()) :: t()

See MishkaGervaz.Form.Web.State.Default.update/2.

wizard_mode?(state)

@spec wizard_mode?(t()) :: boolean()

See MishkaGervaz.Form.Web.State.Default.wizard_mode?/1.