Resolution and dispatch for the pluggable components module.
DynamicForm renders inputs, labels, and errors through
DynamicForm.CoreComponents by default. Applications can point the library
at their own components module — typically the Phoenix-generated
MyAppWeb.CoreComponents — either globally:
config :dynamic_form, components: MyAppWeb.CoreComponentsor per form:
<DynamicForm.form id="contact-form" components={MyAppWeb.CoreComponents}>The per-form attribute wins over the config; without either, the built-in module renders everything.
The contract, and per-function fallback
Dispatch is per function: each component the renderer needs is looked up on
the configured module with function_exported?/3, falling back to the
built-in implementation when the module doesn't define it. A module only
needs to define the functions it wants to own:
| Function | Renders | Phoenix-generated CoreComponents? |
|---|---|---|
input/1 | text, email, number, textarea, select, checkbox controls (receives field, type, label, and per-type extras like options/prompt/multiple) | yes — works out of the box |
input_radio_group/1 | radiogroup and rating questions | no — built-in fallback |
input_checkbox_group/1 | multi-select checkbox groups | no — built-in fallback |
label/1, error/1 | around custom-control slot bodies | no (private in Phoenix 1.8) — built-in fallback |
section/1 | panels | no — built-in fallback |
nested_entry/1 | the container around each repeating nested-form entry (receives index, name, and the entry contents as inner_block) | no — built-in fallback |
button/1 | the submit button | yes — delegates when exported |
translate_error/1 | error messages (routes through the app's Gettext) | yes — delegates when exported |
Delegation is intentionally NOT a blanket "send everything to input/1":
a Phoenix-generated input/1 ends in a catch-all clause, so an unknown
type like "radio-group" would render a broken <input type="radio-group">
instead of raising. The named-function contract keeps every control either
correctly delegated or correctly built-in.
Summary
Functions
Whether the components module provides its own fun/1 implementation.
Renders the component fun with assigns, delegating to the components
module when it exports the function and falling back to
DynamicForm.CoreComponents otherwise.
Resolves the components module: the per-form value when given, otherwise
the :components application config, otherwise nil (built-in only).
Translates an error tuple, delegating to the components module's
translate_error/1 when exported — routing messages through the
application's own Gettext — and falling back to the built-in translation
with the given Gettext backend otherwise.
Functions
Whether the components module provides its own fun/1 implementation.
Renders the component fun with assigns, delegating to the components
module when it exports the function and falling back to
DynamicForm.CoreComponents otherwise.
Resolves the components module: the per-form value when given, otherwise
the :components application config, otherwise nil (built-in only).
Raises ArgumentError when the module cannot be loaded, so a typo fails
loudly instead of silently falling back to the built-in components.
Translates an error tuple, delegating to the components module's
translate_error/1 when exported — routing messages through the
application's own Gettext — and falling back to the built-in translation
with the given Gettext backend otherwise.