LiveViewNative.LiveForm.Component (live_view_native_live_form v0.4.0-rc.1)
View SourceBase components for LiveForm
Summary
Functions
Renders a form.
Renders nested form inputs for associations or embeds.
Checks if the input field was used by the client.
Functions
Renders a form.
This form component behaves identital to Phoenix.Component.form/1
. Please
refer to that docmentation for usage details. on arguments. This component
is platform agnostic. The <LiveForm>
element will be implemented in all
supported clients. Third-party clients can also implement their own <LiveForm>
element and take advantage of this component.
Attributes
for
(:any
) (required) - An existing form or the form source data.action
(:string
) - The action to submit the form on. This attribute must be given if you intend to submit the form to a URL without LiveView.as
(:atom
) - The prefix to be used in names and IDs generated by the form. For example, settingas: :user_params
means the parameters will be nested "user_params" in yourhandle_event
orconn.params["user_params"]
for regular HTTP requests. If you set this option, you must capture the form with:let
.csrf_token
(:any
) - A token to authenticate the validity of requests. One is automatically generated when an action is given and the method is notget
. When set tofalse
, no token is generated.errors
(:list
) - Use this to manually pass a keyword list of errors to the form. This option is useful when a regular map is given as the form source and it will make the errors available underf.errors
. If you set this option, you must capture the form with:let
.method
(:string
) - The HTTP method. It is only used if an:action
is given. If the method is notget
norpost
, an input tag with name_method
is generated alongside the form tag. If an:action
is given with no method, the method will default topost
.multipart
(:boolean
) - Setsenctype
tomultipart/form-data
. Required when uploading files.Defaults to
false
.Global attributes are accepted. Additional HTML attributes to add to the form tag. Supports all globals plus:
["autocomplete", "name", "rel", "enctype", "novalidate", "target"]
.
Slots
inner_block
(required) - The content rendered inside of the form tag.
Renders nested form inputs for associations or embeds.
Attributes
field
(Phoenix.HTML.FormField
) (required) - A %Phoenix.HTML.Form{}/field name tuple, for example: {@form[:email]}.id
(:string
) - The id to be used in the form, defaults to the concatenation of the given field to the parent form id.as
(:atom
) - The name to be used in the form, defaults to the concatenation of the given field to the parent form name.default
(:any
) - The value to use if none is available.prepend
(:list
) - The values to prepend when rendering. This only applies if the field value is a list and no parameters were sent through the form.append
(:list
) - The values to append when rendering. This only applies if the field value is a list and no parameters were sent through the form.skip_hidden
(:boolean
) - Skip the automatic rendering of hidden fields to allow for more tight control over the generated markup.Defaults to
false
.options
(:list
) - Any additional options for thePhoenix.HTML.FormData
protocol implementation.Defaults to
[]
.
Slots
inner_block
(required) - The content rendered for each nested form.
Examples
<.form
:let={f}
phx-change="change_name"
>
<.inputs_for :let={f_nested} field={f[:nested]}>
<.input type="text" field={f_nested[:name]} />
</.inputs_for>
</.form>
Dynamically adding and removing inputs
Dynamically adding and removing inputs is supported by rendering named buttons for
inserts and removals. Like inputs, buttons with name/value pairs are serialized with
form data on change and submit events. Libraries such as Ecto, or custom param
filtering can then inspect the parameters and handle the added or removed fields.
This can be combined with Ecto.Changeset.cast/3
's :sort_param
and :drop_param
options. For example, imagine a parent with an :emails
has_many
or embeds_many
association. To cast the user input from a nested form, one simply needs to configure
the options:
schema "mailing_lists" do
field :title, :string
embeds_many :emails, EmailNotification, on_replace: :delete do
field :email, :string
field :name, :string
end
end
def changeset(list, attrs) do
list
|> cast(attrs, [:title])
|> cast_embed(:emails,
with: &email_changeset/2,
sort_param: :emails_sort,
drop_param: :emails_drop
)
end
Here we see the :sort_param
and :drop_param
options in action.
Note:
on_replace: :delete
on thehas_many
andembeds_many
is required when using these options.
When Ecto sees the specified sort or drop parameter from the form, it will sort the children based on the order they appear in the form, add new children it hasn't seen, or drop children if the parameter instructs it to do so.
The markup for such a schema and association would look like this in SwiftUI:
<.inputs_for :let={ef} field={@form[:emails]}>
<LiveHiddenField name="mailing_list[emails_sort][]" value={ef.index} />
<.input type="text" field={ef[:email]} placeholder="email" />
<.input type="text" field={ef[:name]} placeholder="name" />
<LiveButton
type="submit"
name="mailing_list[emails_drop][]"
value={ef.index}
phx-click={Native.dispatch("change")}
>
<.icon name="hero-x-mark" class="w-6 h-6 relative top-2" />
</LiveButton>
</.inputs_for>
<LiveHiddenField name="mailing_list[emails_drop][]" />
<Button name="mailing_list[emails_sort][]" value="new" phx-click={Native.dispatch("change")}>
add more
</Button>
We used inputs_for
to render inputs for the :emails
association, which
contains an email address and name input for each child. Within the nested inputs,
we render a hidden mailing_list[emails_sort][]
input, which is set to the index of the
given child. This tells Ecto's cast operation how to sort existing children, or
where to insert new children. Next, we render the email and name inputs as usual.
Then we render a button containing the "delete" text with the name mailing_list[emails_drop][]
,
containing the index of the child as its value.
Like before, this tells Ecto to delete the child at this index when the button is
clicked. We use phx-click={Native.dispatch("change")}
on the button to tell LiveView
to treat this button click as a change event, rather than a submit event on the form,
which invokes our form's phx-change
binding.
Outside the inputs_for
, we render an empty mailing_list[emails_drop][]
input,
to ensure that all children are deleted when saving a form where the user
dropped all entries. This hidden input is required whenever dropping associations.
Finally, we also render another button with the sort param name mailing_list[emails_sort][]
and value="new"
name with accompanied "add more" text. Please note that this button must
have type="button"
to prevent it from submitting the form.
Ecto will treat unknown sort params as new children and build a new child.
This button is optional and only necessary if you want to dyamically add entries.
You can optionally add a similar button before the <.inputs_for>
, in the case you want
to prepend entries.
Checks if the input field was used by the client.
This function conforms exactly the same as Phoenix.Component.used_input?/1