Is there a way to put a tiny heex template directly in a controller action?

I have an app that has a few pages where the html is just a single tag with an id that a javascript app gets loaded into. I do want those pages to load with the standard layouts/app.html.heex layout, but it seems silly to me to have a separate function in a separate file for just <div id="app-target"></div>. Is there a way to call something like the Phoenix.Controller.html function in a way that you can (1) pass it some .heex sigil code, and (2) have it render with the app’s layout included?

Basically, I want to be able to do something like this:

defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller

  def show(conn, _params) do
    conn
    |> heex(~H"""
    <div id="app-target"></div>
    """
    , assign_1: "some assign for app.html.heex layout", assign_2: "some other assign for app.html.heex layout")
  end
end

Is this possible?

If you have a variable called assigns you can do heex.

This code or something close to it should be valid if you import the sigil:

assigns = %{foo: 123}

my_heex = ~H"""
<%= @foo %>
"""

Then assign to your Conn my_heex. I can’t test right now as I’m far from a computer though

2 Likes

Just as @lubien said!
Only it would return %Phoenix.LiveView.Rendered{} struct which can be converted to IO data to send to client via Phoenix.HTML.Safe.to_iodata/1

import Phoenix.Component

assigns = %{foo: 123}
rendered = ~H"<%= @foo %>"
content = Phoenix.HTML.Safe.to_iodata(rendered)
text(conn, content)
4 Likes

Ooh, interesting! Thank you both for this. I’ve got a cute little function that looks like this now that seems to be working really well:


      def heex(conn, heex_function), do: heex(conn, %{}, heex_function)

      def heex(conn, assigns, heex_function) do
        assigns = Map.merge(conn.assigns, assigns)
        content = heex_function.(assigns) |> Phoenix.HTML.Safe.to_iodata()

        html(conn, content)
      end
1 Like

For anyone who’s curious, I have not figured out how to get this function to apply the app’s normal layouts in the traditional way with this technique, but I did figure out a decent workaround. I changed the <%= @inner_content %> line in app.html.heex and root.html.heex from this

    <%= @inner_content %>

to this

    <%= if assigns[:inner_content] do %>
      <%= @inner_content %>
    <% else %>
      <%= render_slot(@inner_block) %>
    <% end %>

and now when I call the heex function, I can just add them around the heex template explicitly so the end result looks like this:

    conn
    |> heex(
      %{title: "Page title"},
      fn assigns ->
        ~H"""
        <Layouts.root page_title={@title}>
          <Layouts.app>
            <div id="app-target"></div>
          </Layouts.app>
        </Layouts.root>
        """
      end
    )

and it seems to be working out just fine.

1 Like