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

In general, the book isn’t yet updated for Phoenix version 1.6. On page 18 of the book, the authors indicate that an auto generated of router.ex should contain:

scope "/", PentoWeb do
    pipe_through :browser
    live "/", PageLive, :index
end

This code is no longer generated. Specifically, the line:

live "/", PageLive, :index

This is troubling since this occurs in the “Getting to Know LiveView” section, the very section that should be easy to understand for first time users.

1 Like

Hi @Charles! We’re in the process of updating the book for the latest Phoenix and LiveView projects. The next release, which anyone who has already purchased the book will have access to, will be fully up to date. As you can imagine, there are a lot of changes to accommodate, so stay tuned for the up-to-date version in the next few months.

3 Likes

Hi, any idea when the book will be updated - unfortunately impossible to follow-along at the moment.
Thanks.

Hello! We’re hard at work with the final round of revisions today so keep an eye out for the next Beta release soon–aiming for sometime in January if all goes according to plan.

2 Likes

Hey there! The same about the installation of phx_gen_auth, - it was merged into Phoenix 1.6, so you don’t need to add its dependency anymore to the mix.exs file.

Yep that change will be reflected too! The new version should be out in the next few days in fact :slight_smile:

2 Likes

Hi! Seems like page 56 is missing an assign, should be:

  def mount(_params, session, socket) do
    {
      :ok,
      assign(
        socket,
        score: 0,
        message: "Guess a number.",
        session_id: session["live_socket_id"],
        current_user: Pento.Accounts.get_user_by_session_token(session["user_token"])
      )
    }
  end

Otherwise, the Liveview crashes.

Hi there! I think you might be looking at an outdated version of the book. The latest release that went out a few days ago has a code snippet that does include the assigns. However this made me realize that there is a new error in that code snippet, which is that I did not include the current_user: assigns. So, make sure you have the latest version of the book but also keep an eye out for the new fix in the next release.

Thanks!

I actually just purchased the book so this is on the current version. I think I might have written it weird but what you described is exactly what I meant ::smiley:

Looking forward to the next versions!

I’m now at the “Live Uploads” chapter and stumbled upon another issue. I’m getting this error:

warning: consuming uploads requires a return signature matching:

    {:ok, value} | {:postpone, value}

got:

    "/images/live_view_upload-1644006235-875637336798847-7"

when attempting to upload a file. The file input element gets reset.

I attempted to fix it by changing the upload_static_file/2’s return value to {:ok, Routes.static_path(socket, "/images/#{Path.basename(dest)}")} but I get the same behavior but without the error. I don’t have much experience with LV yet so I’m not sure what else I could try but I thought to give a heads-up in case it’s not something on my end.

Edit: this is in the latest version of the book (B6.0)

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!

Thanks for helping out! I tried the new code and it worked. Though, I had to modify {:ok, path} = to path = since it was returining without an :ok tuple. I’m indeed running 0.17.5, too.

As a separate note, page 175 uses this line as an example attrs = %{gender: "bruce", year_of_birth: 1965, user_id: 1} but using “bruce” as a gender causes the changeset to be invalid.

Thanks again, I’ll update that too!

1 Like