Form does not display errors for nested embeds while using `:let`
rmoorman opened this issue · comments
When using nested ecto embeds in a live view through form
and inputs_for
, while simultaneously using the :let
binding, errors for the nested embed no longer show up in the browser.
I don't know if that is to be expected or if this is a symptom of a problem with the let binding or the form component for example.
I came across this problem while fiddling around a bit with the example code for issue phoenixframework/phoenix#5901.
For demonstration purposes, I created another example repo which can be used to reproduce this specific issue.
The problem
The problem basically comes down to the following:
In the screenshot above, there are 3 embeds visible in the form, all of them have errors in the name
fields, but only the first and the third embed show the error.
I did the same to all fields, clicked inside and typed another letter to try to trigger the error display.
Below the fields, I printed the form struct for reference where the errors can be seen.
The trigger
The problem is triggered when using the form
, inputs_for
, and input
like this:
<.form :let={f} for={@form} phx-change="change">
<.inputs_for :let={nested} field={f[:nested_with_let]}>
<.input field={nested[:name]} label="Name" />
</.inputs_for>
</.form>
The :let
is used on the form to bind the form to f
, which is then used to provide the field to inputs_for
and somewhere there seems to be the culprit.
At first glance, there does not seem to be a problem with inputs_for
's behavior. I added a more deeply nested example to the example repo (using the @form
assign directly instead of f
for the first level), and nesting inputs_for
twice, while binding the form struct does seem to work there.
How to reproduce
As mentioned, there is an example repository that can be used to reproduce this problem. There, I bootstrapped a new phoenix application and added the example liveview in hello/lib/hello_web/live/form_live.ex which includes the schema and the template.
Errors are only shown after you attempt to insert/update the changeset. You can try calling Ecto.Changeset.apply_action
or similar.
The nested changesets do have an action (:insert
) and the main changeset has an action set as well. The screenshot in the issue description above was taken after the form fields of the nested embeds were changed and the matching handle_event
callback (from phx-change
) ran the changeset functions.
The example code does set an action using the action
option of to_form
but setting an action on the changeset does not seem to make any difference.
Regarding apply_action
, this is happening in the (live) validation phase. It does happen on phx-submit
as well though (where all errors should be shown because used/unused fields are not considered while rendering the form). I updated the example code with a submit handler. Furthermore I left out the form result handling part (persistence) to keep the example code more focused.
The problem is, that the validation errors show for the nested embeds, when <.inputs_for :let={nested} for={@form[:nested]}>
is used, but are not rendered, when the :let
binding from the <form :let={f} form={@form}>
is used.
Looking at the documentation for :let
usage with form
it seems that in any case the "form" should be returned. The documentation refers explicitly to changesets that are converted to a form struct on the fly (presumably what to_form
in your liveview would do). So I assumed that passing a form to the form component and then capturing it with :let
and passing that value on instead of the @form
assign would just work.
Maybe you are wondering why I would use the :let
binding in the first place when I already have a form? I was copy and pasting around form code (re-organizing fields) and I thought I would like to try if it felt more consistent to also use let just as I do with the inputs_for
for the embeds.
I am also aware that the documentation states that using the let binding is discouraged because "LiveView can better optimize your code if you access the form fields using @form[:field]
rather than through the let-variable form". But from that I would still assume that it at least works, just like it does with <.inputs_for>
.
Of course there is a "work-around" (as just using the assign also works), and it is by no means a huge problem, but I thought it would make more sense to report it nevertheless.