phoenix_html icon indicating copy to clipboard operation
phoenix_html copied to clipboard

Nested errors not handled for map's FormData implementation

Open LostKobrakai opened this issue 1 year ago • 11 comments

With the new FormData implementation for plain maps I tried to set errors on nested inputs, but it looks like errors are not handled for nested inputs.

LostKobrakai avatar Mar 05 '23 20:03 LostKobrakai

Any ideas on how it should be handled? Thoughts and PRs are welcome!

josevalim avatar Mar 05 '23 20:03 josevalim

Not concretely. I was looking for (more) documentation when I discovered it's not handled yet. The only thing I found was that it's supposed to be a Keyword list, but I guess one_to_many also needs some kind of indexing / matching length lists.

LostKobrakai avatar Mar 05 '23 20:03 LostKobrakai

So currently errors seem to be a expected to be a list of [{field :: String.t | atom, error :: term}].

Nesting could be added to that in different ways:

  • Expecting a (single) list item per parent within that list: {parent_field, [{inner_field :: String.t | atom, error :: term}]} There's the complexity of how to deal with multiple parent keys in the errors list as well as differenciating cases, where we set an error on the parent, vs it holding errors of nested fields.
  • Changing the field key of the tuple to allow nested fields: [{field :: String.t | atom | [String.t | atom], error :: term}]
# First option
errors = [
  {"title", "is invalid"},
  {"title", "must be larger than 5"},
  {
    "parent",
    [
      {"inner", "is invalid"},
      {"inner", "must be larger than 5"},
    ]
  }
]

# Second option
errors = [
  {"title", "is invalid"},
  {"title", "must be larger than 5"},
  {["parent", "inner"], "is invalid"},
  {["parent", "inner"], "must be larger than 5"}
]

I feel like the second option would be the cleaner one, but open to discussion or input on this one.

LostKobrakai avatar Mar 17 '23 09:03 LostKobrakai

Thanks. The issue with this approach is that we enlarge the contract for everyone. The root of the problem is that a map (or an atom or a conn) does not provide enough information for nested error handling, so I am more inclined to say you will need a more complete data-structure for this, such as changesets (or anything similar to them).

josevalim avatar Mar 17 '23 10:03 josevalim

Then I'm wondering how useful it is to support nesting for those structures in the first place. Not supporting error handling severly limits the usefulness of a form integration and to me this is all the reason why I never even bothered using the atom/conn implementation in the past.

With the recent updates and seemingly more push to make using maps to power forms usable I had hoped these drawbacks would've been addressed – especially as I'm interested in forms, where I don't need to use atoms as keys, which rules out using changesets (without some external mapping).

Is there any appetite to have a more feature complete implementation in phoenix_html, or would it be better for me to bite the bullet and go with a custom implementation for my usecase?

LostKobrakai avatar Mar 17 '23 11:03 LostKobrakai

The goal of adding maps was to provide a less confusing API than for={:foo} as={:this_is_ambiguous} params={map}, in favor of for={params} as={:foo} which mirrors changesets too. Plus getting rid of the Plug dependency.

It doesn't mean we can't improve it but I hope this provides some background (which means you should probably bite the bullet, sorry!).

josevalim avatar Mar 17 '23 11:03 josevalim

Ok, I guess I'll one follow up question then. Can you expand a bit on where / which contract you see "enlarged for everyone" trying to support nested errors for maps? If I can avoid touching those parts then maybe there's a chance to port back the things I do into phoenix_html.

LostKobrakai avatar Mar 17 '23 11:03 LostKobrakai

As you said, errors is defined as [{field :: String.t | atom, error :: term}] and if we want to support nesting through the errors themselves, we would have to enlarge it. This is not an issue with Ecto because the nested errors is kept in a separate field.

josevalim avatar Mar 17 '23 11:03 josevalim

This was already extended recently to support string field names over just atom field names, so I'm wondering how much this is a concern. We for sure don't want to jump the gun on what format would be appropriate for nested errors, but that doesn't look like changes couldn't be made.

LostKobrakai avatar Mar 17 '23 11:03 LostKobrakai