# ============================================================================= # AshFormBuilder - Complete Integration Guide # ============================================================================= # # This comprehensive guide demonstrates how to integrate AshFormBuilder into # your Phoenix + Ash application with the "Invisible UI" philosophy: # # "The developer defines the business logic in Ash, and the UI is a side effect." # # ## Table of Contents # # 1. Installation & Setup # 2. Resource Definition with Form DSL # 3. Domain Configuration (Code Interfaces) # 4. Phoenix LiveView Integration # 5. Creatable Combobox (Create on-the-fly) # 6. Theme Customization & Adapters # 7. Search Handling for Combobox # 8. Areas of Enhancement # # ============================================================================= # ============================================================================= # 1. INSTALLATION & SETUP # ============================================================================= # ───────────────────────────────────────────────────────────────────────────── # 1.1 Add Dependencies (mix.exs) # ───────────────────────────────────────────────────────────────────────────── # defp deps do # [ # {:ash, "~> 3.0"}, # {:ash_phoenix, "~> 2.0"}, # {:phoenix_live_view, "~> 1.0"}, # {:phoenix, "~> 1.7"}, # {:ash_form_builder, path: "../ash_form_builder"}, # Or from hex when published # # # Optional: UI Component Libraries # {:mishka_chelekom, "~> 0.0.8"}, # For MishkaTheme # {:daisy_ui, "~> 4.0"} # For Tailwind components (optional) # ] # end # ───────────────────────────────────────────────────────────────────────────── # 1.2 Configure Theme (config/config.exs) # ───────────────────────────────────────────────────────────────────────────── # config :ash_form_builder, :theme, AshFormBuilder.Theme.MishkaTheme # # or # config :ash_form_builder, :theme, AshFormBuilder.Themes.Default # # or your custom theme # config :ash_form_builder, :theme, MyAppWeb.CustomTheme # ───────────────────────────────────────────────────────────────────────────── # 1.3 Add Extension to Resource # ───────────────────────────────────────────────────────────────────────────── # In your Ash Resource, add the extension: # # use Ash.Resource, # domain: MyApp.Healthcare, # extensions: [AshFormBuilder] # ← Add this # ============================================================================= # 2. RESOURCE DEFINITION WITH FORM DSL # ============================================================================= defmodule MyApp.Healthcare.Clinic do @moduledoc """ Clinic resource demonstrating AshFormBuilder with many-to-many relationships. ## Form Auto-Inference Fields are automatically inferred from the action's `accept` list: | Ash Type | UI Type | |-------------------|-------------------------| | `:string` | `:text_input` | | `:integer` | `:number` | | `:boolean` | `:checkbox` | | `:date` | `:date` | | `:datetime` | `:datetime` | | `:enum` | `:select` | | `many_to_many` | `:multiselect_combobox` | """ use Ash.Resource, domain: MyApp.Healthcare, extensions: [AshFormBuilder] # ─────────────────────────────────────────────────────────────────────────── # Attributes # ─────────────────────────────────────────────────────────────────────────── attributes do uuid_primary_key :id attribute :name, :string do allow_nil? false description "The clinic's official name" end attribute :address, :string do allow_nil? false end attribute :phone, :string do constraints match: ~r/^\+?[\d\s-]+$/ end attribute :email, :string attribute :website, :string attribute :is_active, :boolean, default: true timestamps() end # ─────────────────────────────────────────────────────────────────────────── # Relationships # ─────────────────────────────────────────────────────────────────────────── relationships do # Many-to-many: Auto-rendered as :multiselect_combobox many_to_many :specialties, MyApp.Healthcare.Specialty do through MyApp.Healthcare.ClinicSpecialty source_attribute_on_join_resource :clinic_id destination_attribute_on_join_resource :specialty_id end # Many-to-many with creatable support (create new items on-the-fly) many_to_many :tags, MyApp.Healthcare.Tag do through MyApp.Healthcare.ClinicTag source_attribute_on_join_resource :clinic_id destination_attribute_on_join_resource :tag_id end end # ─────────────────────────────────────────────────────────────────────────── # Actions # ─────────────────────────────────────────────────────────────────────────── actions do defaults [:create, :read, :update, :destroy] create :create do accept [:name, :address, :phone, :email, :website, :is_active] # Manage relationships separately manage_relationship :specialties, :specialties, type: :append_and_remove manage_relationship :tags, :tags, type: :append_and_remove end update :update do accept [:name, :address, :phone, :email, :website, :is_active] manage_relationship :specialties, :specialties, type: :append_and_remove manage_relationship :tags, :tags, type: :append_and_remove end end # ─────────────────────────────────────────────────────────────────────────── # Validations & Policies # ─────────────────────────────────────────────────────────────────────────── validations do validate present([:name, :address]) validate string_length(:name, min: 3, max: 100) end policies do policy action_type(:create) do authorize_if actor_present() end policy action_type(:update) do authorize_if actor_present() end end # =========================================================================== # ASH FORM BUILDER DSL - Declarative Form Configuration # =========================================================================== # # The `form` block defines how your form should render. Fields not declared # here are auto-inferred from the action's `accept` list. # # =========================================================================== form do # Required: Target action action :create # Optional: Customize submit button submit_label "Create Clinic" # Optional: CSS class for fields wrapper wrapper_class "space-y-6" # Optional: HTML id for the