Formex is an extensible form library for Phoenix. With this library you don't write changeset (as in Ecto), but a separate module that declares fields of form (like in Symfony).
You can also use it with Ecto - see formex_ecto. That library will build changeset and additional Ecto queries for itself.
Formex doesn't validate data for itself - it uses validation libraries instead.
Formex comes with helper functions for templating. For now there is only a Bootstrap 3 form template, but you can easily create your own templates.
In addition to the main library, you have to install some validator adapter. In this example we will use Vex. List of available adapters
mix.exs
def deps do
[{:formex, "~> 0.6.0"},
{:formex_vex, "~> 0.1.0"}]
end
def application do
[applications: [:formex]]
end
config/config.exs
config :formex,
validator: Formex.Validator.Vex,
translate_error: &AppWeb.ErrorHelpers.translate_error/1, # optional, from /lib/app_web/views/error_helpers.ex
template: Formex.Template.BootstrapHorizontal, # optional, can be overridden in a template
template_options: [ # optional, can be overridden in a template
left_column: "col-sm-2",
right_column: "col-sm-10"
]
web/web.ex
def controller do
quote do
use Formex.Controller
end
end
def view do
quote do
use Formex.View
end
end
Let's create a form for article.
# /web/model/article.ex
defmodule App.Article do
defstruct [:title, :content, :hidden]
end
# /web/form/article_type.ex
defmodule App.ArticleType do
use Formex.Type
def build_form(form) do
form
|> add(:title, :text_input, label: "Title", validation: [presence: true])
|> add(:content, :textarea, label: "Content", phoenix_opts: [
rows: 4
], validation: [presence: true])
|> add(:hidden, :checkbox, label: "Is hidden?", required: false)
|> add(:save, :submit, label: "Submit", phoenix_opts: [
class: "btn-primary"
])
end
end
Please note that required
option is used only to generate an asterisk.
Any validation must be done via validation
option.
The :text_input
and so on are function names from
Phoenix.HTML.Form
def new(conn, _params) do
form = create_form(App.ArticleType, %Article{})
render(conn, "form.html", form: form)
end
def create(conn, %{"article" => article_params}) do
App.ArticleType
|> create_form(%Article{}, article_params)
|> handle_form
|> case do
{:ok, article} ->
# do something with a new article struct
{:error, form} ->
# display errors
render(conn, "form.html", form: form)
end
end
form.html.eex
<%= formex_form_for @form, article_path(@conn, :create), [class: "form-horizontal"], fn f -> %>
<%= if @form.submitted? do %>Oops, something went wrong!<% end %>
<%= formex_row f, :title %>
<%= formex_row f, :content %>
<%= formex_row f, :hidden %>
<%= formex_row f, :save %>
<%# or generate all fields at once: formex_rows f %>
<% end %>
Put an asterisk to required fields:
.required .control-label:after {
content: '*';
margin-left: 3px;
}
The final effect after submit:
- Creating forms
- Usage in a controller
- Usage in a template
- Validation
- Nested forms
- Collections of forms
- Add new items to collection on the backend
- Using a select picker plugin with ajax search
- Uploading files with Arc.Ecto (Formex.Ecto)
- formex_ecto - Ecto integration
- formex_vex - Vex
- formex_ecto - Ecto.Changeset