AshFormBuilder.Infer
(AshFormBuilder v0.2.0)
View Source
Zero-Config Auto-Inference Engine for AshFormBuilder v0.2.0.
Dynamically infers complete form field definitions from Ash resource metadata.
Reads action definitions, attributes, arguments, and relationships via Ash.Resource.Info.
Key Capabilities
- Complete Type Mapping - Maps all Ash 3.0 types to appropriate UI components
- Smart Defaults - Infers labels, required status, placeholders from constraints
- Relationship Detection - Auto-detects and configures many_to_many, has_many, belongs_to
- Constraint Awareness - Detects one_of, enums, and other constraints for UI selection
- Configurable Ignoring - Exclude fields without writing full field blocks
- Manage Relationship Support - Infers UI based on manage_relationship configuration
Usage
# Zero-config - infer everything from action.accept
fields = AshFormBuilder.Infer.infer_fields(MyApp.Task, :create)
# Custom ignore list
fields = AshFormBuilder.Infer.infer_fields(MyApp.Task, :create,
ignore_fields: [:id, :tenant_id, :deleted_at]
)
# Include timestamps
fields = AshFormBuilder.Infer.infer_fields(MyApp.Task, :create,
include_timestamps: true
)
# Customize relationship UI
fields = AshFormBuilder.Infer.infer_fields(MyApp.Task, :create,
many_to_many_as: :select,
belongs_to_as: :combobox
)Type Inference Table
| Ash Type | Constraint | Inferred UI | Example |
|---|---|---|---|
:string | - | :text_input | Standard text |
:ci_string | - | :text_input | Case-insensitive |
:text | - | :textarea | Multi-line |
:boolean | - | :checkbox | Toggle |
:integer | - | :number | Whole numbers |
:float / :decimal | - | :number | Decimals |
:date | - | :date | Date picker |
:datetime | - | :datetime | DateTime picker |
:atom | one_of: | :select | Dropdown |
:enum module | - | :select | Enum values |
:email | - | :email | Email input |
:url | - | :url | URL input |
:phone | - | :tel | Telephone |
many_to_many | - | :multiselect_combobox | Searchable multi-select |
has_many | - | :nested_form | Dynamic nested forms |
belongs_to | - | :select | Foreign key selection |
Relationship Handling
When a relationship is detected in the action's accept list:
- many_to_many →
:multiselect_comboboxwith searchable selection - has_many → Nested form configuration (not a direct field)
- belongs_to →
:selector:comboboxbased on destination
For many_to_many relationships, the following field properties are populated:
type: :multiselect_comboboxrelationship: :relationship_namerelationship_type: :many_to_manydestination_resource: DestinationResourceopts: [label_key: :name, value_key: :id, search_event: "...", ...]
This enables seamless UI rendering with searchable multi-select for related records.
Summary
Functions
Detects field type (attribute or relationship) for a given field name.
Detects required preloads for update/destroy actions.
Infers Field structs for the given resource action with zero-config operation.
Infers complete form schema including nested forms and preload requirements.
Functions
Detects field type (attribute or relationship) for a given field name.
Returns
:attribute- Field is a resource attribute{:relationship, relationship}- Field is a relationship:ignore- Field should be ignored
Examples
iex> Infer.detect_field_type(MyApp.Task, :title)
:attribute
iex> Infer.detect_field_type(MyApp.Task, :tags)
{:relationship, %Ash.Resource.Relationships.ManyToMany{...}}
@spec detect_required_preloads([AshFormBuilder.Field.t()], module(), atom()) :: [ atom() ]
Detects required preloads for update/destroy actions.
Identifies which relationships need preloading for form rendering (e.g., many_to_many relationships that appear in the form).
Examples
iex> Infer.detect_required_preloads(fields, MyApp.Task, :update)
[:tags, :assignees]
@spec infer_fields(module(), atom(), keyword()) :: [AshFormBuilder.Field.t()]
Infers Field structs for the given resource action with zero-config operation.
Options
:ignore_fields- List of field names to skip (default:[:id, :inserted_at, :updated_at]):include_timestamps- Include timestamp fields (default:false):many_to_many_as- UI type for many_to_many (default::multiselect_combobox):has_many_as- UI type for has_many (default::nested_form):belongs_to_as- UI type for belongs_to (default::select):creatable- Enable creatable combobox for many_to_many (default:false):create_action- Action for creating new items (default::create)
Returns
List of %AshFormBuilder.Field{} structs in rendering order.
Examples
# Basic zero-config inference
iex> fields = Infer.infer_fields(MyApp.Task, :create)
[%Field{name: :title, type: :text_input, ...}, ...]
# Custom ignore list
iex> fields = Infer.infer_fields(MyApp.Task, :create, ignore_fields: [:tenant_id])
[%Field{...}]
# Include timestamps
iex> fields = Infer.infer_fields(MyApp.Task, :create, include_timestamps: true)
[%Field{name: :inserted_at, ...}, %Field{name: :updated_at, ...}]
# Creatable combobox for relationships
iex> fields = Infer.infer_fields(MyApp.Task, :create, creatable: true)
[%Field{name: :tags, type: :multiselect_combobox, opts: [creatable: true]}]
@spec infer_schema(module(), atom(), keyword()) :: %{ fields: [AshFormBuilder.Field.t()], nested_forms: keyword(), action: atom(), resource: module(), required_preloads: [atom()] }
Infers complete form schema including nested forms and preload requirements.
Returns a map suitable for passing to AshPhoenix.Form.for_create/3.
Returns
%{ fields: [Field.t()], nested_forms: keyword(), action: atom(), resource: module(), required_preloads: [atom()] }
Examples
iex> schema = Infer.infer_schema(MyApp.Task, :create)
%{
fields: [%Field{...}],
nested_forms: [subtasks: [type: :list, resource: MyApp.Subtask]],
action: :create,
resource: MyApp.Task,
required_preloads: [:tags, :assignees]
}