skovmand

skovmand

Easiest way to render a .heex template to an HTML string?

Usually in tests of my views, I have used safe_to_string() to convert a returned :safe tuple to HTML that I can assert on. For example:

test "greets the user" do
  user = %{name: "Kurt"}
  html = MyView.render("some_template.html", user: user) |> Phoenix.HTML.safe_to_string()

  assert html =~ "Hello, Kurt"
end

However, if the template is a .heex file, this no longer works because the return struct is a %Phoenix.LiveView.Rendered{} struct, e.g.:

%Phoenix.LiveView.Rendered{
  dynamic: #Function<0.64041902/1 in PeanutButterWeb.PageView."hello.html"/1>,
  fingerprint: 29760870404026531075951007545402085092,
  root: true,
  static: ["<p>Hello, Kurt</p>"]
}

This is also true, even if I’m rendering from a plain view.

Is there a simple way to convert this struct to plain html?

Thanks :+1:

Marked As Solved

skovmand

skovmand

Okay, here’s what I found after digging around for a while:

test "greets the user" do
  user = %{name: "Kurt"}
  html = MyView.render("some_template.html", user: user)
        |> Phoenix.HTML.Safe.to_iodata()
        |> IO.iodata_to_binary()

  assert html =~ "Hello, Kurt"
end

Will give the returned HTML as string, ready for assertions.

By the way, there are very good docs available about it here: Phoenix.LiveView.Engine — Phoenix LiveView v1.2.5

Also Liked

petrus-jvrensburg

petrus-jvrensburg

Just to add: this also works for rendering components to plain HTML, without having to define a separate template / view, for example:

DevApp.ProductComponent.render(%{product: %{id: 1}}) 
|> Phoenix.HTML.Safe.to_iodata() 
|> IO.iodata_to_binary()
sreyansjain

sreyansjain

You can try

Phoenix.Template.render_to_string(<module_name>, function name in string, "html", assigns)

for example

Phoenix.Template.render_to_string(MyAppWeb.TaskHTML, "show_task", "html", task: task)
rhcarvalho

rhcarvalho

Nice finding, thanks for sharing!

I found one more usable API that achieves the same, with the advantage of a compilation error in case the template name/function is invalid:

MyModule.my_function_component(%{some: assign_value})
|> Phoenix.LiveViewTest.rendered_to_string()

Phoenix.LiveViewTest.rendered_to_string/1’s implementation is, no surprise, similar to the first solution in this thread:

  def rendered_to_string(rendered) do
    rendered
    |> Phoenix.HTML.html_escape()
    |> Phoenix.HTML.safe_to_string()
  end

Example usage

Passing some HTML code to my template, for example an embed code that the user can copy:

  def mount(%{"id" => id}, _session, socket) do
    socket =
      socket
      |> assign(
        id: id,
        embed_code: embed_code(%{id: id}) |> Phoenix.LiveViewTest.rendered_to_string()
        # embed_code: Phoenix.Template.render_to_string(__MODULE__, "embed_code", "html", %{id: id})
      )

    {:ok, socket}
  end

  attr :id, :string, required: true

  def embed_code(assigns) do
    ~H"""
    ...<%= @id %>...
    """
  end

Last Post!

rhcarvalho

rhcarvalho

I could not figure out how to skip the annotations when they are globally enabled in dev. Would you have an example of how to skip them?

Having the annotations in dev is not problem since they won’t be there in prod, but your comment got me curious :slight_smile:


In my testing everything rendered off a ~H"""...""" HEEx template in an environment with annotations enabled gets the annotations. IIUC that’s expected because sigil_H/2 is a macro that injects the annotations at compilation time.

https://github.com/phoenixframework/phoenix_live_view/blob/v0.20.17/lib/phoenix_component.ex#L791-L807

It’s the first time I’m looking at this code, so excuse me if I make unsound conclusions.

My reading is that sigil_H/1 calls EEx.compile_string/2 passing Phoenix.LiveView.HTMLEngine. The former will do the annotations based on Application.get_env(:phoenix_live_view, :debug_heex_annotations, false).

Where Next?

Popular in Questions Top

Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

Other popular topics Top

baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31494 112
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

We're in Beta

About us Mission Statement