Forms with live_file_input consume files when saving even if form is invalid

Hi,
I have a form to edit a model with name and images attributes. Name is a string (min 3 chars), images is an array of string.
I’m using the example code from the official docs to implement the multi file upload using live_file_input to populate the images field.

When clicking on the submit button the save event handler is triggered, consuming files and copying them to the static folder.

If the form has no errors, the changeset validations are correct and the model is persisted.
If the form has an error (for example when the name is less than 3 chars):

  • the files are consumed
  • then the changeset validations are executed and the changeset is marked invalid
  • then the invalid changeset is re-rendered again, so that the user can fix the errors, but by now the files selected are gone (as they are already consumed).

What I have learned:

  • live_file_input does a preflight request when submitting that, if there is an error in the live_file_input, it does nothing and the submit is aborted until errors are fixed. This doesn’t take into account any other error on the form.
  • I might do the changeset validation on the save handler and only then try to consume the files and save the model, but it feels like the validate handler is the place where this logic should be, not the save handler
  • Maybe a boolean option might be useful in the allow_uploads to enable/disable saving. We could use it to control the live_file_input submit behavior from the live_view side by forcing the preflight checks to abort the submit if it is set to false. Something like:
     # on mount/update:
       is_valid = true
      ....
       |> allow_upload(:images,
         accept: ~w(.png .jpg .jpeg),
         max_entries: @max_entries,
         max_file_size: @max_file_size,
         valid: is_valid
       )}
    
     # on validate:
       is_valid = some && business && validation
       ...
       |> allow_upload(:images,
         valid: is_valid
       )}
    

I have a working example that shows this behavior here [1]. Just go to localhost:4000/products, add two images and put a name with one char to see the validation error on the name field and submit. The page reloads but the files are gone.

Is there a way for the save operation to honor both form errors and live_file_input errors before attempting the consumption of files and saving the model?

Thanks in advance for your insights

[1] GitHub - miguelcoba/mercury at official-live-file-input-docs

1 Like

:valid isn’t a known option :slight_smile: (EDIT: sorry reading comprehension on my part, it was a suggestd feature :). You need to conditionally consume the file only if the data is valid on your handle_event("save".

I don’t think such a feature makes sense because you can and should only consume the file based on the validity/and or successful persistence of your data. So standard Elixir primitives apply with if/else/case etc.

1 Like

Hey, Chris, thanks, that’s what I suspected. I’ll do it that way!

Thanks for the fast response.

I’ve just seen that Miguel himself already asked this.

Still, some ideas:

  • Having two changesets: one for all fields except images, and other for images (or all fields including images). This also helps with getting the id on creation so you can use that in the final file destination path, if you want.
  • This doesn’t solve the cases in which some field(s) depend on the files field.

I am really struggling with this, any help will be apreciated.

1 Like

You can surround the consume_uploaded_entries() call by a check to see if the changeset is valid. Or you can consume the entries when the input has been persisted in the database (in the {:ok, _} branch of the case in the save function).

Yeah, I am using Multi with two separate changesets and it helps a bit. Still, there are edge cases like when you submit an empty create form: all fields display validation errors except for the uploads.

Maybe you can catch that with a seperate error_tag for the uploader. Then, provide seperate progress function in allow_upload that updates the changeset with the ref of the file that was selected.

1 Like

Yes, I think that for uploads we have to diverge a bit from the usual patterns. What I wonder is if this is avoidable and will be “fixed” in the future or it’s just the way it is due to the nature of uploads.