Ash Form Builder ๐Ÿš€

โš ๏ธ EXPERIMENTAL - USE AT YOUR OWN RISK โš ๏ธ

This package is under active development. API may change without notice. Breaking changes are likely in minor versions. Not recommended for critical production systems without thorough testing.

Status: Alpha/Experimental | Version: 0.1.0

A declarative form generation engine for Ash Framework and Phoenix LiveView.

Define your form structures directly inside your Ash.Resource domain layer, and let the engine automatically generate the Phoenix form modules, nested configurations, and LiveView components.

โœจ Features


๐Ÿ“ฆ Installation

Add ash_form_builder to your list of dependencies in mix.exs:

def deps do
  [
    {:ash, "~> 3.0"},
    {:ash_phoenix, "~> 2.0"},
    # โš ๏ธ EXPERIMENTAL - Use at your own risk
    {:ash_form_builder, "~> 0.1.0"}
    # Or from GitHub for latest:
    # {:ash_form_builder, github: "nagieeb0/ash_form_builder"}
  ]
end

Then run:

mix deps.get

Configure your theme in config/config.exs:

# Default HTML theme
config :ash_form_builder, :theme, AshFormBuilder.Themes.Default

# MishkaChelekom theme (requires mishka_chelekom dependency)
config :ash_form_builder, :theme, AshFormBuilder.Theme.MishkaTheme

๐Ÿš€ Quick Start

1. Define Your Resource with Auto-Inference

The simplest approach: just declare the action, and the form fields are auto-inferred from your resource's accept list.

defmodule MyApp.Billing.Clinic do
  use Ash.Resource,
    domain: MyApp.Billing,
    extensions: [AshFormBuilder]  # โ† Add this extension

  attributes do
    uuid_primary_key :id
    attribute :name, :string, allow_nil?: false
    attribute :address, :string
    attribute :phone, :string
  end

  relationships do
    many_to_many :doctors, MyApp.Billing.Doctor do
      through MyApp.Billing.ClinicDoctor
      source_attribute_on_join_resource :clinic_id
      destination_attribute_on_join_resource :doctor_id
    end
  end

  actions do
    defaults [:create, :read, :update, :destroy]
  end

  form do
    action :create
    # Fields are auto-inferred from action.accept!
    # many_to_many relationships automatically use :multiselect_combobox
  end
end

2. Configure Domain Code Interfaces

Define form_to_<action> interfaces in your Domain for clean LiveView integration:

defmodule MyApp.Billing do
  use Ash.Domain

  resources do
    resource MyApp.Billing.Clinic do
      define :form_to_create_clinic, action: :create
      define :form_to_update_clinic, action: :update
    end
  end
end

3. Use in LiveView

defmodule MyAppWeb.ClinicLive.Form do
  use MyAppWeb, :live_view

  alias MyApp.Billing

  def mount(_params, _session, socket) do
    # Zero manual AshPhoenix.Form calls!
    form = Billing.Clinic.Form.for_create(actor: socket.assigns.current_user)
    {:ok, assign(socket, form: form)}
  end

  def render(assigns) do
    ~H"""
    <.live_component
      module={AshFormBuilder.FormComponent}
      id="clinic-form"
      resource={MyApp.Billing.Clinic}
      form={@form}
    />
    """
  end

  def handle_info({:form_submitted, MyApp.Billing.Clinic, clinic}, socket) do
    {:noreply, push_navigate(socket, to: ~p"/clinics/#{clinic.id}")}
  end
end

๐ŸŽฏ Key Features

Auto-Inference Engine

Fields are automatically mapped from Ash types to UI components:

Ash Type UI Type
:string:text_input
:integer:number
:boolean:checkbox
:date:date
:datetime:datetime
:atom + one_of:select
many_to_many:multiselect_combobox

Creatable Combobox โญ NEW

Allow users to create new related records on-the-fly:

form do
  field :tags do
    type :multiselect_combobox
    label "Tags"
    placeholder "Search or create tags..."
    
    opts [
      creatable: true,              # โ† Enable creating new items
      create_action: :create,
      create_label: "Create \"\"",
      search_event: "search_tags",
      debounce: 300,
      label_key: :name,
      value_key: :id
    ]
  end
end

Nested Forms (has_many)

Dynamic add/remove for child records:

form do
  nested :subtasks do
    label "Subtasks"
    cardinality :many
    add_label "Add Subtask"
    
    field :title do
      label "Subtask"
      required true
    end
  end
end

๐Ÿ“š Documentation

Guides

Comprehensive guides are available in the guides/ directory:

  1. Todo App Integration - Complete step-by-step tutorial
  2. Relationships Guide - has_many vs many_to_many, filtering, limits
  3. Example Usage - Reference documentation with all features

Module Documentation

Generate local docs:

mix docs

โš ๏ธ Experimental Status

This package is EXPERIMENTAL and under active development.

What This Means

For Production Use

If you choose to use this in production:

  1. Pin to exact version: {:ash_form_builder, "== 0.1.0"}
  2. Test thoroughly before deployment
  3. Monitor the repository for updates and breaking changes
  4. Be prepared to handle breaking changes on upgrade
  5. Consider contributing fixes and improvements

Roadmap to 1.0

Planned features for stable release:


๐Ÿ”ง Configuration

Theme System

AshFormBuilder uses a theme system for UI customization:

# config/config.exs

# Default theme (semantic HTML)
config :ash_form_builder, :theme, AshFormBuilder.Themes.Default

# MishkaChelekom theme (requires mishka_chelekom dependency)
config :ash_form_builder, :theme, AshFormBuilder.Theme.MishkaTheme

# Custom theme (implement AshFormBuilder.Theme behaviour)
config :ash_form_builder, :theme, MyAppWeb.CustomTheme

Creating Custom Themes

See example_usage.ex for a complete custom theme example.


๐Ÿงช Testing

Run the test suite:

mix test

๐Ÿค Contributing

Contributions are welcome! This is an experimental project, and community feedback will help shape its development.

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

๐Ÿ“„ License

MIT License - see LICENSE file for details.


๐Ÿ™ Acknowledgments


Built with โค๏ธ using Ash Framework