Formex v0.6.7 Formex.Type behaviour

In order to create a form, you need to create the Type file. It’s similar to Symfony’s way of creating forms.

Example:

defmodule App.ArticleType do
  use Formex.Type
  alias Formex.Ecto.CustomField.SelectAssoc

  def build_form(form) do
    form
    |> add(:title, :text_input, label: "Title")
    |> add(:content, :textarea, label: "Content", phoenix_opts: [
      rows: 4
    ])
    |> add(:hidden, :checkbox, label: "Is hidden", required: false)
    |> add(:category_id, :select, label: "Category", phoenix_opts: [
      prompt: "Select a category"
    ], choices: [
      "Category A": 1,
      "Category B": 2
    ])
    |> add(:save, :submit, label: if(form.struct.id, do: "Edit", else: "Add"), phoenix_opts: [
      class: "btn-primary"
    ])
  end
end

The :text_input and so on are function names from Phoenix.HTML.Form

Nested forms

We have models User, and UserInfo. The UserInfo contains extra information that we don’t want, for some reason, store in the User model.

defmodule App.User do
  defstruct [:first_name, :last_name, user_info: %App.UserInfo{}]
end
defmodule App.UserInfo do
  defstruct [:section, :organisation_cell]
end

Our form will consist of two modules:

user_type.ex

defmodule App.UserType do
  use Formex.Type

  def build_form(form) do
    form
    |> add(:first_name, :text_input)
    |> add(:last_name, :text_input)
    |> add(:user_info, App.UserInfoType, struct_module: App.UserInfo)
  end
end

user_info_type.ex

defmodule App.UserInfoType do
  use Formex.Type

  def build_form( form ) do
    form
    |> add(:section, :text_input)
    |> add(:organisation_cell, :text_input)
  end
end

Our form can be displayed in various ways:

  • Print whole form, with nested form, at once

    <%= formex_rows f %>
  • Standard

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <%= formex_nested f, :user_info %>
  • Set a form template for nested form

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <div class="form-horizontal">
      <%= formex_nested f, :user_info, template: Formex.Template.BootstrapHorizontal %>
    </div>
  • Use your render function

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <%= formex_nested f, :user_info, fn subform -> %>
      <%= formex_row subform, :section %>
      <%= formex_row subform, :organisation_cell %>
    <% end %>

Collections of forms

Installation

To start using this functionality you have to include the JS library.

package.json

"dependencies": {
  "formex": "file:deps/formex",
}

After modifying package.json you have to run npm install again.

JavaScript

import {Collection} from 'formex'

Collection.run() // run listening clicks on Add and Delete buttons

// if you need to do something when user adds or deletes an item:
Collection.run(function() {
  // run again your fancy scripts after a new item was added
})

Model

We have models User, and UserAddress.

defmodule App.User do
  defstruct [:first_name, :last_name, user_addresses: []]
end
defmodule App.UserAddress do
  defstruct [:id, :street, :city, :formex_id, :formex_delete]
end

Keys :id, :formex_id and :formex_delete are important in collection child.

Form Type

Our form will consist of two modules:

user_type.ex

defmodule App.UserType do
  use Formex.Type

  def build_form(form) do
    form
    |> add(:first_name, :text_input)
    |> add(:last_name, :text_input)
    |> add(:user_addresses, App.UserAddressType, struct_module: App.UserAddress)
  end
end

user_address_type.ex

defmodule App.UserAddressType do
  use Formex.Type

  def build_form( form ) do
    form
    |> add(:street, :text_input)
    |> add(:city, :text_input)
  end
end

Template

Our collection can be displayed in various ways:

  • Print whole form, with collection, at once

    <%= formex_rows f %>
  • Standard

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <%= formex_collection f, :user_addresses %>
  • Set a form template for collection

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <div class="form-horizontal">
      <%= formex_collection f, :user_addresses, template: Formex.Template.BootstrapHorizontal %>
    </div>
  • Use your render function

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    <%= formex_collection f, :user_addresses, [template: Formex.Template.BootstrapHorizontal],
      fn collection -> %>
      <div class="form-horizontal">
        <%= formex_collection_items collection %>
        <%= formex_collection_add collection, "Add" %>
      </div>
    <% end %>
  • You can also set a render function for collection item

    <%= formex_row f, :first_name %>
    <%= formex_row f, :last_name %>
    
    <% collection = fn collection -> %>
      <div class="form-horizontal">
        <%= formex_collection_items collection %>
        <%= formex_collection_add collection, "Add" %>
      </div>
    <% end %>
    
    <% collection_item = fn subform -> %>
      <%= formex_collection_remove {:safe, "&times;"}, "Are you sure you want to remove?" %>
      <%= formex_row subform, :street %>
      <%= formex_row subform, :city %>
    <% end %>
    
    <%= formex_collection f, :user_addresses, [template: Formex.Template.BootstrapHorizontal],
    collection, collection_item %>

For more info about rendering see Formex.View.Collection.formex_collection/5

Result with some additional HTML and CSS:

Summary

Functions

Adds a form item to the form. May be a field, custom field, button, or subform

Callbacks

In this callback you have to add fields to the form

Functions

add(form, name)
add(form, name, opts)
add(form, name, type_or_module, opts)
add(form :: Form.t, name :: Atom.t, type_or_module :: Atom.t, opts :: Map.t) :: Form.t

Adds a form item to the form. May be a field, custom field, button, or subform.

type_or_module is :text_input by default (so the Formex.Field.create_field/3 is used).

Behaviour depends on type_or_module argument:

Callbacks

build_form(form)
build_form(form :: Formex.Form.t) :: Formex.Form.t

In this callback you have to add fields to the form.