Updating the socket causes live_file_upload to fail?

Can someone let me know why it is live_file_upload fails if the socket is updated outside of the form?

I basically have a form that contains a file upload and other values.

One of the values is taken from the socket, and is updated outside of the form.

If I update the socket value, the live_file_upload no longer works
If I don’t update the socket value the live_file_upload works fine. I can set a default value and have no issues.

If I upload a file and then update the socket I get the below

[error] GenServer #PID<0.5968.0> terminating
** (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.

    (phoenix_live_view 0.18.18) lib/phoenix_live_view/upload.ex:19: Phoenix.LiveView.Upload.allow_upload/3

Any ideas?

Hey @GazeIntoTheAbyss can you show the code you have in your component?

Sorry for the delay, I stripped it down. “Example” is the value being updated.

In my index I have this for the form

defp apply_action(socket, :example, _params) do
    example = %Example{}
    socket
    |> assign(:example, example)
  end

In the index I can update the socket value for example using this:

  def handle_event("change example", %{"id" => id}, socket) do
    example = Examples.get_example!(id)
    {:noreply, socket |> assign(:example, example)}
  end

My form info

<.live_component
    module={ExampleWeb.ExampleLive.FormComponent}
    id={"example"}
    action={@live_action}
    post={@post}
    example={@example}
    patch={~p"/example"}
/>
@impl true
def update(%{post: post} = assigns, socket) do
  changeset = Posts.change_post(post)

  {:ok, socket
    |> assign(assigns)
    |> assign(:changeset, changeset)
    |> assign(:uploaded_files, [])
    |> allow_upload(:image, accept: ~w(.png .jpeg .jpg), max_entries: 5, max_file_size: 4_194_304, external: &S3Upload.presign_upload/2)
  }
end

@impl true
def handle_event("validate", %{"post" => post_params}, socket) do
  changeset =
    socket.assigns.post
    |> Posts.change_post(post_params)
    |> Map.put(:action, :validate)

  {:noreply, assign(socket, :changeset, changeset)}
end
```<.live_file_input upload={@uploads.image} />

I can update the socket example value and save it to the post with no issues on its own
I can upload a file and save it to the post with no issues on its own.

If I do both however, in either order, it will cause the file_upload to not work.

Other than the error I originally posted I have no feedback with regards to whats going wrong.

If I remove

example={@example}

From the form, I can update the socket and upload files, but I obviously cannot save the example ID as a socket.assigns value.

This is your issue here.

update will be called mulitple times, each time the parent live view updates an assign. You should call this line in the mount callback of the component, not the update call.

This sounds right, but I’m kind of lost on what I need to do. I normally just create forms in the basic template you get from the mix generator.

Does this mean I need to add a mount to my form, and put the allow_uploads there? Or do I need to put the allow_upload at a higher level?

Sorry for the noob questions

Yes, you would add:


def mount(socket) do
  socket = allow_upload(socket, :image, accept: ~w(.png .jpeg .jpg), max_entries: 5, max_file_size: 4_194_304, external: &S3Upload.presign_upload/2)

  {:ok, socket}
end

to your live component.

1 Like

Thank you so much.

Tried so many things over the last couple of hours and this was all I needed lol. Couldn’t find anything about how to mount in a live_component and didn’t even think it was possible until this. Thanks

Definitely I would recommend just reading the phoenix live component docs top to bottom, they are pretty thorough and cover a lot that helps in situations like this.

1 Like