Converts <:field>, <:group>, and <:nested> slot entries from
DynamicForm.form/1 into a DynamicForm.Instance struct.
This is the slot-mode counterpart to DynamicForm.Instance.Decoder: the
decoder 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 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.Renderercanrender_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
convert!/1 raises ArgumentError with a descriptive message for invalid
definitions: 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 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).