MishkaGervaz.Form.Web.State (MishkaGervaz v0.0.2)

Copy Markdown View Source

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

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

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(),
  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

apply_presentation(state, assigns)

@spec apply_presentation(t(), map()) :: t()

Applies a mount's own presentation choices over the resource's — see the __using__ version.

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.