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, "×"}, "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
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:
if it’s a module and
- implements
Formex.CustomField
, theFormex.CustomField.create_field/3
is called - implements
Formex.Type
, theFormex.Form.register/4
is called (it’s for nested forms and collections of forms)
- implements
- if it’s
:submit
or:reset
, theFormex.Button.create_button/3
is called - otherwise it’s a normal field and the
Formex.Field.create_field/3
is called.
Callbacks
In this callback you have to add fields to the form.