A Phoenix.HTML.FormField may be passed as argument,
which is used to retrieve the input name, ID, and values.
Otherwise all attributes may be passed explicitly.
Maturity: Developing
Usage
Custom types
You can register additional input types at build time with the types
option.
build_field(
types: %{"ranked" => &MyAppWeb.Inputs.ranked/1}
)
The additional types can be rendered like any other types.
<.field field={@form[:rank]} type="ranked" label="Rank" />
The map is merged into the built-in types. You can set an entry to nil to
remove a built-in type you do not use, and to :default to keep the
built-in control, for example after a shared configuration replaced it.
build_field(
types: %{
"select" => &MyAppWeb.Inputs.select/1,
"color" => nil,
"week" => nil
}
)
Types that render a group
Sometimes a control needs to render multiple inputs, for example a date
field with separate selects for each segment. Set group: true on such
types, so that the controls are wrapped inside a fieldset with a legend.
build_field(
types: %{
"permissions" => {&MyAppWeb.Inputs.permissions/1, group: true}
}
)
The fieldset has the class #{base_class}-#{type}. Errors and
description are rendered outside of the fieldset.
The options assign can be used to pass additional options to your custom
type. The only requirement is that it is a list.
The field component renders the label, the errors and the description as it
does for any other type, and calls your function component for the control.
Your component receives these assigns:
| Assign | |
|---|---|
| name | the input name, with [] appended when multiple is set |
| id | the id attribute, from id or the form field |
| value | the field value |
| type | the type you registered |
| options | the options the caller passed, or nil |
| prompt | the prompt the caller passed, or nil |
| multiple | whether the field takes more than one value |
| invalid | whether the field has errors, for aria-invalid |
| describedby | for aria-describedby, nil without a description |
| errormessage | for aria-errormessage, nil without errors |
| validations | the validation attributes derived from the changeset |
| rest | the global attributes the caller passed |
The component you referenced renders the control:
attr :name, :string, required: true
attr :id, :string, required: true
attr :value, :any, required: true
attr :type, :string, required: true
attr :options, :list, required: true
attr :prompt, :string, required: true
attr :multiple, :boolean, required: true
attr :invalid, :boolean, required: true
attr :describedby, :string, required: true
attr :errormessage, :string, required: true
attr :validations, :list, required: true
attr :rest, :global, doc: "Any additional HTML attributes."
def ranked(assigns) do
~H"""
<div class="ranked" data-type={@type}>
<select
name={@name}
id={@id}
aria-describedby={@describedby}
aria-errormessage={@errormessage}
aria-invalid={@invalid && "true"}
{@validations}
{@rest}
>
<option
:for={n <- 1..5}
value={n}
selected={to_string(n) == to_string(@value)}
>
{n}
</option>
</select>
</div>
"""
end
Types
In addition to all HTML input types, the following type values are also
supported:
"select"
"checkbox-group"
"radio-group"
"switch"
Class and Global Attribute
Note that the class attribute is applied to the outer container, while
the rest global attribute is applied to the <input> element.
Gettext
To translate field errors as well as the required_text and optional_text
using Gettext, set the gettext_module option when building the component:
build_field(gettext_module: MyApp.Gettext)
Label positioning
The component does not provide an attribute to modify label positioning
directly. Instead, label positioning should be handled with CSS. If your
application requires different label positions, such as horizontal and
vertical layouts, it is recommended to add a modifier class to the form.
For example, the default style could position labels above inputs. To place
labels to the left of the inputs in a horizontal form layout, you can add an
is-horizontal class to the form:
<.form class="is-horizontal">
<!-- inputs -->
</.form>
Then, in your CSS, apply the necessary styles to the .field class within
forms having the is-horizontal class:
form.is-horizontal .field {
// styles to position label left of the input
}
The component has a hide_label attribute to visually hide labels while
still making them accessible to screen readers. If all labels within a form
need to be visually hidden, it may be more convenient to define a
.has-visually-hidden-labels modifier class for the <form>.
<.form class="has-visually-hidden-labels">
<!-- inputs -->
</.form>
Ensure to take checkbox and radio labels into consideration when writing the
CSS styles.
Examples
<.field field={@form[:name]} />
<.field field={@form[:email]} type="email" />
Radio group and checkbox group
The radio-group and checkbox-group render groups of radio buttons or
checkboxes with a single component invocation. The options attribute is
required for these types and has the same format as the options for the
select type, except that options may not be nested.
<.field
field={@form[:email]}
type="checkbox-group"
label="Cuisine"
options={[
{"Mexican", "mexican"},
{"Japanese", "japanese"},
{"Libanese", "libanese"}
]}
/>
Note that the checkbox-group type renders an additional hidden input with
an empty value before the checkboxes. This ensures that a value exists in
case all checkboxes are unchecked. Consequently, the resulting list value
includes an extra empty string. While Ecto.Changeset.cast/3 filters out
empty strings in array fields by default, you may need to handle the
additional empty string manual in other contexts.
Keyboard
Space - toggle a checkbox or a switch.
Left, Right, Up and Down - move between the radios of a group and
check the one the focus lands on.
The controls are native elements, so the browser handles these.