Parses <:field>, <:group>, and <:nested> slot entries from
DynamicForm.form/1 into a DynamicForm.Instance struct.
This is the slot-mode counterpart to DynamicForm.Parser.FromData: the
JSON parser normalizes untrusted external data (JSON, string-keyed maps), while
this module normalizes compiler-produced slot entries (atom-keyed maps).
Both produce the same %Instance{} structs consumed by the rest of the
library.
Conversion rules
<:field>entries convert in template order — question types (["text", "comment", "dropdown", "radiogroup", "checkbox", "boolean", "rating", "tagbox", "file"]) becomeInstance.Questionstructs;html,image, andcustombecomeInstance.Elementstructs.- Snake_case slot attrs map to the SurveyJS-style struct fields
(
visible_if→visibleIf,label→title,options→choices, ...). - Flattened validation attrs (
min_length,max_length,min,max,pattern,format) buildInstance.Validatorstructs; thevalidatorsattr accepts explicit%Validator{}structs or atom-keyed maps for anything the flattened attrs can't express. - A field with
group="name"is collected into apanelelement declared by a<:group name="name">entry. The panel is emitted at the position of its first member field. A<:group>can itself declaregroup=to sit inside another panel, and a parent takes the position of its earliest member — including one contributed by a child group. - A field with
nested="name"is collected into thetemplateElementsof apaneldynamicquestion declared by a<:nested name="name">entry.nesteddeclares the field's data scope (its value lives inside each entry of the nested form's list value);groupdeclares visual grouping within that scope — the two combine, and a group inside a nested form declares the samenestedscope on its own declaration. See the Nested Forms guide. - An entry with a slot body keeps the raw slot entry in the struct's
:slotfield soDynamicForm.Renderer.Componentcanrender_slot/2it. Bodies are in-memory only — they are dropped when the instance is JSON-encoded.
Naming and scoping
Field and <:nested> names are data keys, unique per scope (the form
level is one scope; each nested form's template is another) — so a
top-level name and an addresses[].name coexist, mirroring the
application's schema. <:group> and <:nested> declaration names are
reference targets for the flat group=/nested= attrs, so declarations
are globally unique.
Validation
parse!/1 raises ArgumentError with a descriptive message for invalid
definitions — see DynamicForm.Parser.FromComponent.Validator for the full
set of checks: missing names, duplicate names within a scope, unknown
types, choice questions without options, custom fields without a body,
references to undeclared groups or nested forms, group/member nested-scope
mismatches, nested forms with no members, and cyclic <:nested> or
<:group> references.
Summary
Functions
Validates slot entries and builds a %DynamicForm.Instance{}.
Functions
Validates slot entries and builds a %DynamicForm.Instance{}.
Expects the assigns of DynamicForm.form/1: :id plus the :field,
:group, and :nested slot lists (and optional :title and
:description).