Liveview modal components fail in Phoenix 1.6 when called using live_patch

My Liveview modal components are failing to open if they are called using live_patch within a span but work when called using a button with a handle-event.

I have a Liveview app that I upgraded to Phoenix 1.6.6. I have followed all of the directions laid out in this link. The app was fully functional before the upgrade:

Phoenix 1.5.x to 1.6 upgrade instructions

My app fails when I call certain modal components using a live_patch. I get this compilation error: “Stateful components must return a HEEx template (~H sigil or .heex extension)”

Yep, I’ve made sure that I replaced the ~L with ~H AND I’ve made sure that my stateful components have a div. I get this error with every single modal component whether it is stateful or stateless. I also get this error whether I put the template in an heex file or if it is defined within the .ex file.

BUT … I don’t get this error if I call the modal component via a button click that has a handle-event.

Here is what works and what doesn’t work:

FAILS (both Show and Save fail)

@impl true
  def render(assigns) do
    ~H"""
    <div id="resources">
      ...
<%= if @list_resources_action == "index" do %>
                  <span><%= live_patch "Show",
                      to: Routes.resourceform_index_path(@socket,
                      :show, resourceform) %></span><br>
                  <span><%= live_redirect "Save",
                      to: Routes.resourceform_save_resource_path(@socket,
                      :save, resourceform.id ) %></span>
                <% end %>
    ...
    </div>
"""
end

WORKS
If I call the modal component this way, then it works:

@impl true
  def render(assigns) do
  ~H"""
  <div id="authors">
...
<%= if @list_authors_action == "index" do %>
      <div class="flex-shrink-0 w-10 h-10">
           <button phx-click="show"
                phx-target={@myself}
                phx-value-id={author.id}>
                <img class="w-full h-full" src="/images/show_button.png" title="Show">
         </button>
     </div>
<% end %>
...
</div>
"""

#*****************************************************************
  # handle_event :show (glasses button)
  #*****************************************************************
  @impl true
  def handle_event("show", %{"id" => id}, socket) do
    {:noreply, push_redirect(socket, to: Routes.author_show_author_path(socket, :show_author, id), replace: true)}
  end

end

I must be missing something. Are there a set of instructions for upgrading Liveview applications to Phoenix 1.6? Was I supposed to do something else to my modal components?

If it’s a live component, aren’t you setting the id outside of the component already?

There is an upgrade guide linked at the bottom of the announcement blog post.

Thanks for the link. I had read through that before but didn’t see anything in there about live_patch and live_redirect changing.

Was your question referencing the div id? I just threw that in there to see if it would fix the problem. It did not. I took it out and I’m still getting the ~H error.

All of this code was based on the code that used to be auto-generated with LiveView. It’s just a table with “show” and “save” in the last column.

You might want to show the full code.

UPDATE
I have figured out that it is only live_modal that fails. As soon as I change it to live_component, everything works. I’m going to close this out and start a new post that more accurately describes the problem.

SOLUTION:
Arg … stupid mistake. I completely missed fixing modal_component.ex. That was what causing all of the problems. Once I updated that with the Phoenix 1.6 requirements, modal worked again.

1 Like

And for anyone looking for the solution for a LiveView Modal 1.5 to 1.6 upgrade:

modal_component.ex needs to look more like:

@impl true
def render(assigns) do
    ~H"""
    <div id="{ @id }" class="phx-modal"
        phx-capture-click="close"
        phx-window-keydown="close"
        phx-key="escape"
        phx-target="#{ @id }"
        phx-page-loading>

        <div id="modal-content" class="phx-modal-content">
            <%= live_patch raw("&times;"), to: @return_to, class: "phx-modal-close" %>
            <%= live_component @component, @opts %>
        </div>
    </div>
    """
end

And then I was getting errors errors about the form_component.html.heex until I hacked it to look like the following. Note:
1st error was because all the code needs to be wrapped in < div > tags.
2nd error was about SafeHTML. That went away when I changed it to the new <.form /> format.

<div>
    <h2><%= @title %></h2>

    <.form
        let={f}
        for={@changeset}
        id="user-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save">

        <%= label f, :name %>
        <%= text_input f, :name %>
        <%= error_tag f, :name %>

        <%= label f, :age %>
        <%= number_input f, :age %>
        <%= error_tag f, :age %>

        <div>
            <%= submit "Save", phx_disable_with: "Saving..." %>
        </div>
    </.form>
</div>

I hope that saves someone some time searching with AskJeeves