How to reuse/reset live upload?

I have a page that allows uploads via a modal. When you submit the form, it closes the modal.

The problem is that now I want to reuse that modal to upload a new file. If I simply show the modal again, it’s using the same file input and thus has a file associated with it already.

If I call allow_upload again in my LiveView, it complains that the upload needs to be consumed or cancled, which I already did via consume_uploaded_entries/3.

Here’s my code…

def handle_event("save", _params, socket) do
  consume_uploaded_entries(socket, :avatar, fn file, entry ->
    {:ok, file}
  end)

  socket = allow_upload(socket, :avatar, accept: ~w(.jpg .jpeg .png .svg), max_entries: 1)

  {:noreply, socket}
end

And here’s the error:

** (ArgumentError) cannot allow_upload on an existing upload with active entries.

Use cancel_upload and/or consume_upload to handle the active entries before allowing a new upload.

How can I reset the uploads so I can use them again on the same live view page?

1 Like

Hmm, haven’t tested this but maybe try using disallow_upload/2 to first revoke the previously allowed upload before re-allowing it?

def handle_event("save", _params, socket) do
  consume_uploaded_entries(socket, :avatar, fn file, entry ->
    {:ok, file}
  end)

  socket = 
    socket
    |> disallow_upload(socket, :avatar)
    |> allow_upload(:avatar, accept: ~w(.jpg .jpeg .png .svg), max_entries: 1)

  {:noreply, socket}
end

Alternatively if you wanted to keep previous “rounds” of uploads, you could potentially use a :round assign to pass allow_uploads a dynamic name e.g.

def handle_event("save", _params, socket) do
  consume_uploaded_entries(socket, :avatar, fn file, entry ->
    {:ok, file}
  end)

  socket = 
    socket
    |> assign(:round, socket.assigns.round + 1)
    |> allow_upload(
      String.to_atom("avatar_#{socket.assigns.round}"),
      accept: ~w(.jpg .jpeg .png .svg), max_entries: 1
    )

  {:noreply, socket}
end

I tried that and it didn’t work. I also tried a few other things and ultimately gave up and kinda redesigned my whole page.

I kinda want to make an example repo and get community feedback for what the best way to do this is. It’s such a common pattern: user edit page, but allow for avatar cropping, rotating, editing via a modal and using cropperjs.

I got it all working, and tbh, the code is actually pretty nice and clean. It does use a Phx Hook, but the Javascript is surprisingly small. The problem was that it took me three days to get there.

1 Like

Hmm, was your goal was to let users choose between a modal to edit the existing uploadeds avatar image and another to upload a new avatar image to replace the one they had previously uploaded?

Regardless, glad you got something working in the end and I’d be interested in seeing what you came up with if you do end up making an example repo.

This same issue has come up in the past. It seems as though there was a deliberate design decision not allow resetting uploads. The reasoning isn’t clear (to me at least).

“I also tried a few other things and ultimately gave up and kinda redesigned my whole page.” “The problem was that it took me three days to get there.”

I’m kinda at that point. Take the simple use case of a form that includes a live_file_input. It can be set, reset, or blanked out. For the case of blanking it out, I need to reset the model field that holds the file reference to nil. The user should still be able to continue filling out the form. They should also be able to choose to add a file again. I’ve yet to find anything to make that work because I can’t reset the upload.

I’ve also got a has_many “lines” in the form. Each line can have one file. The parent live view is responsible for defining the uploads and passing them into the stateful child component. The upload is named to include a value for a virtual attribute that allows me to identify which line it is. So in that case, preserving the upload name is critical, or I don’t know which line the file is for. I should be able to choose a file, blank out the file, or choose a different file on update. The “blank out” case similarly seems to require that upload reset.

Can either of these use cases be handled with the live file uploader? Would it make more sense to just do it with JS? Is there a breakdown of intended uses case and known limitations for live file uploader vis a vis these common use cases?

Thanks!