Programming Phoenix LiveView: book not updated to version 1.6 (p. 18)

Hi Pablo! Thanks for pointing this out. Looks like there have been some changes to the upload behavior that require some code changes here. I’ll outline them below and then you can look for the updated code and content in the next book release!

First off, I will mention that I don’t get that warning on my version of the code for this chapter, which is running Phoenix LiveView 0.17.5. Could you let me know what version you’re running?

Regardless, though, here is the fix:

Your handle_progress/3 function in the form component should look like this:

defp handle_progress(:image, entry, socket) do
    :timer.sleep(200)
    if entry.done? do
# the following line has changed
      {:ok, path} =
        consume_uploaded_entry(
          socket,
          entry,
          &upload_static_file(&1, socket)
        )
      {:noreply,
       socket
       |> put_flash(:info, "file #{entry.client_name} uploaded")
# the following line has changed
       |> assign(:image_upload, path)}
    else
      {:noreply, socket}
    end
  end

The upload_static_file/2 function should look like this:

defp upload_static_file(%{path: path}, socket) do
    # Plug in your production image file persistence implementation here!
    dest = Path.join("priv/static/images", Path.basename(path))
    File.cp!(path, dest)
# the following line has changed
    {:ok, Routes.static_path(socket, "/images/#{Path.basename(dest)}")}
  end

And the save_product functions should now take advantage of the new :image_upload socket assignment and add that value to the product params that will be saved:

defp save_product(socket, :edit, product_params) do
# the following line has changed
    case Catalog.update_product(socket.assigns.product, Map.put(product_params, "image_upload", socket.assigns.image_upload)) do
      {:ok, _product} ->
        {:noreply,
         socket
         |> put_flash(:info, "Product updated successfully")
         |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, :changeset, changeset)}
    end
  end

  defp save_product(socket, :new, product_params) do
# the following line has changed
    case Catalog.create_product(Map.put(product_params, "image_upload", socket.assigns.image_upload)) do
      {:ok, _product} ->
        {:noreply,
          socket
          |> put_flash(:info, "Product created successfully")
          |> push_redirect(to: socket.assigns.return_to)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign(socket, changeset: changeset)}
    end
  end

Finally, you can remove this line from the form HTML:

 <%= hidden_input f, :image_upload %>

Since we’re now storing :image_upload directly in socket assigns and pulling it out of socket assigns when its time to save the product.

One thing to note (which is new behavior I believe), is that once the upload entry has been consumed, it is removed from @upload.entries. That is why the form file input resets. While the file input resets, the image upload is successfully stored in socket. assigns.upload (with the new code above).

One additional change you can make to get better feedback in the UI is to render the flash info on the bottom of the form like this:

# put this within the div that contains the form
<p class="alert alert-info" role="alert"
    phx-click="lv:clear-flash"
    phx-value-key="info"><%= live_flash(@flash, :info) %></p>

Then, when a file is finished uploading, you should see something that looks like this:

Let me know if that works for you or if you run into any other difficulties!