tj0

tj0

I’ve been following the steps here for the upgrade from 1.6 to 1.7 and it has gone relatively smoothly all the way till the phoenix_view to phoenix_component switch. verified_routes are quite nifty.

In the past, I’ve found the liveview upgrades / breaking changes to be extremely valuable. I’m not quite sure about the phoenix changes though, but since I’m not sure phoenix_view will be deprecated in the future, I’m willing to put in the legwork to do the migration.

To summarize for those considering the upgrade and all the layout changes:

  • there is no views directory. Not sure where any functions are supposed to go that were in views.
  • liveview templates are colocated in the liveview directory or embedded
  • livecomponent templates are…I’m not sure where.

The two main issues I’m having are

  1. The templates for my LiveViews and LiveComponents are from 200-600 lines. These are far too large to embed into the render/1 function and I’m not sure how to pass on the assigns to the template.

Original

import AppWeb.ChartView
def render(assigns) do
  AppWeb.ChartView.render("show.html", assigns)
end

New one where it’s not indicated how to pass the assigns into the

  use AppWeb, :live_view
  embed_templates "../templates/chart/*"
  def render(assigns) do
      # does not pass the assigns into the show and if they are passed as assigns={assigns} will need a template re-write
        ~H"""
        <.show />
        """
  end

I was hoping for something like the below code, but I cannot call render/2 or Phoenix.LiveView.render/2 because “(module Phoenix.Liveview is not available)”


def render(assigns) do
        ~H"""
        <%= Phoenix.LiveView.render "show.html", assigns %>
        """
end
  1. I’ve put rendering specific functions into the views. App.ChartView has functions for making viewable dates, converting decimals to fractions, capitalization, etc.

I like the idea of getting rid of the views directory as it seemed like mostly boilerplate. However, I can’t figure out how to do an easy migration of the templates to use phoenix_component instead of phoenix_views.

I’m not understanding how to replace the render functions in a way that doesn’t require massive changes to the templates or how to pass in the helper functions for the view itself. What am I missing?

Showing Posts 1 to 10

aiwaiwa

aiwaiwa

My little experiment of merging index.ex and show.ex from the standard live generator ended up looking like this:


  embed_templates "*"

  @impl true
  def render(assigns) do
    assigns =
      assigns
      |> assign_new(:live_action, fn -> :unknown_live_action end)
      |> assign_new(:patch, fn ->
        patch(assigns.live_action, assigns.user)
      end)

    ~H"""
    <%= cond do %>
      <% @live_action in [:index, :new, :index_edit] -> %>
        <.index.index
          streams={@streams}
        />
      <% @live_action in [:show, :show_edit] -> %>
        <.index.show
          user={@user}
        />
      <% true -> %>
        <ProjectWeb.Util.inspect_value v={binding()} />
    <% end %>

    <.modal
      :if={@live_action in [:new, :index_edit, :show_edit]}
      id="user-modal"
      show
      on_cancel={JS.push("cancel") |> JS.patch(@patch)}
    >
      <.live_component
        module={ProjectWeb.UserLive.FormComponent}
        id={@user.id || :new}
        title={@page_title}
        user={@user}
        action={@live_action}
      />
    </.modal>
    """
  end

where index.index and index.show correspond to index.index.html.heex and index.show.html.heex files.

codeanpeace

codeanpeace

Maybe something like:
<%= ChartView.show(assigns) %>

From Migrating to Phoenix.Component | Phoenix.View:

Remove def view and also remove the import Phoenix.View from def html in your lib/my_app_web.ex module. When doing so, compilation may fail if you are using certain functions:

  • Replace render/3 with a function component. For instance, render(OtherView, "_form.html", changeset: @changeset, user: @user) can now be called as <OtherView.form changeset={@changeset} user={@user} />. If passing all assigns, render(OtherView, "_form.html", assigns) becomes <%= OtherView._form(assigns) %>.
  • If you are using Phoenix.View for APIs, you can remove Phoenix.View altogether. Instead of def render("index.html", assigns), use def users(assigns). Instead of def render("show.html", assigns), do def user(assigns). Instead render_one/render_many, call the users/1 and user/1 functions directly.

And from Compartmentalize state, markup, and events in LiveView | Phoenix.LiveView:

However, sometimes you need to compartmentalize or reuse more than markup. Perhaps you want to move part of the state or part of the events in your LiveView to a separate module. For these cases, LiveView provides Phoenix.LiveComponent, which are rendered using live_component/1:

<.live_component module={UserComponent} id={user.id} user={user} />
codeanpeace

codeanpeace

As a heads up, this is a known pitfall:

Due to the scope of variables, LiveView has to disable change tracking whenever variables are used in the template…

Similarly, do not define variables at the top of your render function:

def render(assigns) do
  sum = assigns.x + assigns.y

  ~H"""
  <%= sum %>
  """
end

Instead explicitly precompute the assign in your LiveView, outside of render:

assign(socket, sum: socket.assigns.x + socket.assigns.y)

source: Pitfalls | Assigns and HEEx templates | Phoenix LiveView v0.18.18

aiwaiwa

aiwaiwa

Thank you! Does modifying assigns fall under this category? I saw this pattern in core components

codeanpeace

codeanpeace

Hmm, that’s a good point – not sure if modifying assigns would fall under this. Maybe it’s fine for core components since they’re invoked within the ~H sigil… ¯\_(ツ)_/¯

If I’m understanding the intent correctly, it might still make sense for those assign_new calls to be outside render as the assigns that it ensures exists shouldn’t change across re-renders.

josevalim

josevalim

Creator of Elixir

FWIW, Phoenix View is not deprecated and we are committed to keeping it working in the long term. I will make the docs clearer. :slight_smile:

tj0

tj0 OP

After a bit of fiddling with the syntax, the following seems to work (thanks @codeanpeace , your suggestion was pretty close).

  use AppWeb, :live_component

  import AppWeb.ChartView # for all the rendering helper functions
  embed_templates "../templates/chart/*"

  def render(assigns) do
    ~H"""
    <div>
    <%= show(assigns) %>
    </div>
    """
  end

The existing template has a root div element, however, without wrapping the ~H in a div, we " Stateful components must have a single static HTML tag at the root".

Still not sure how to do conditional rendering yet.

I’m relieved as I’m not sure how risky this upgrade is to be honest. Might have a lot of little gotchas and needs extensive testing.

aiwaiwa

aiwaiwa

So just to reiterate, since core_components define stateless components, and they modify assigns for themselves to render correctly (here’s an example):

This differs from my conditional rendering because it implemenets the render callback of a :live_view?

I’m just trying to formulate a rule of a thumb for such scenarios.

By the way, the suggested

    ~H"""
    <%= cond do %>
      <% @live_action in [:index, :new, :index_edit] -> %>
        <%= index_index(assigns) %>
      <% @live_action in [:show, :show_edit] -> %>
        <% index_show(assigns) %>

strangely does not include <% index_show(assigns) %> at all, just shows an empty page, although it sees embedded component functions. Anything else put within condition shows up, but not the <% index_show(assigns) %>. And all I did was rename the files to eliminate the ..

tj0

tj0 OP

The main issue from my understanding is ensuring updates and change tracking works correctly. In this case, the table is adding row ids based on changing data. The rest of the code isn’t there, but there are two possibilities:

  1. liveview replaces the entire html element
  2. it may not matter what the row number is on a change and it doesn’t change the rendering.

Without testing it, I think your code should work for change tracking. The reason the second conditional doesn’t render is that it needs a <%= , not <% to display.

aiwaiwa

aiwaiwa

LOL you are right! Thanks!

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews