with EEx.function_from_file
I can create a function from an eex-template, call it and get a rendered string back.
How can I do that with ~H
?
with EEx.function_from_file
I can create a function from an eex-template, call it and get a rendered string back.
How can I do that with ~H
?
You want to use a separate .heex.html file:
I have not come across an out of box api in phoenix_live_view to achieve the first.
If you are looking for second one - there is no straight forward way to do this.
Couldn’t you just pass HTMLEngine in the options as engine
?
I want to use ~H
as a template engine to create an HTML file (which I can then process with another tool to PDF). I already have these templates working in a liveview-app, but need some of the views as PDF.
I was hoping I’d find an equivalent to EEx — EEx v1.17.0-dev - example:
# sample.html.heex
<MyComponent.greet name={@name} />
# sample.ex
defmodule Sample do
require HEEx # <--- does not exist
HEEx.function_from_file(:def, :sample, "sample.html.heex", [:a, :b])
end
# iex
Sample.sample(name: "World")
#=> "Hello, World!"
You mean like
EEx.function_from_file(
:def, :sample, "sample.eex", [:assigns], engine: Phoenix.LiveView.HTMLEngine)
this returns a %Phoenix.LiveView.Rendered
and it doesn’t look like I’ll get a HTML from that easily.
You can pipe the %Phoenix.LiveView.Rendered
into
Phoenix.HTML.Safe.to_iodata() |> to_string()
which would return the html resulting string.
indeed. Some times things are so obvious
defmodule Sample do
require EEx
EEx.function_from_file(
:def, :sample, "sample.html.heex", [:assigns], engine: Phoenix.LiveView.HTMLEngine)
end
defmodule MyComponent do
use Phoenix.Component
def greet(assigns) do
~H"""
<p>Hello, <%= assigns.name %></p>
"""
end
end
# sample.html.heex
<MyComponent.greet name={@name} />
iex(1)> Sample.sample(%{name: "World"}) |> Phoenix.HTML.Safe.to_iodata() |> to_string()
"<p>Hello, World</p>"
this (obviously) needs liveview in deps
!
Which is enough though, no other deps needed (Jason if you want LV to stop complaining)
defp deps do
[
{:jason, "~> 1.3.0"}, # recommended by LV
{:phoenix_live_view, "~> 0.17.10"}
]
end
writing to a file does not need to_string
Sample.sample(%{name: "World"})
|> Phoenix.HTML.Safe.to_iodata()
|> then(fn data -> File.write!(path, data) end)
This does not work with LV 0.18, any ideas what to do?
== Compilation error in file lib/sample.ex ==
** (KeyError) key :caller not found in: [file: "lib/sample.ex", line: 23, engine: Phoenix.LiveView.HTMLEngine]
(elixir 1.14.2) lib/keyword.ex:595: Keyword.fetch!/2
(phoenix_live_view 0.18.11) lib/phoenix_live_view/html_engine.ex:163: Phoenix.LiveView.HTMLEngine.init/1
(eex 1.14.2) lib/eex/compiler.ex:295: EEx.Compiler.compile/2
lib/sample.ex:23: (module)
maybe related to this issue
try
EEx.function_from_file(
:def, :sample, "sample.eex", [:assigns], engine: Phoenix.LiveView.HTMLEngine, caller: __ENV__)
adding caller: __ENV__
leads to the next error:
== Compilation error in file lib/sample.ex ==
** (KeyError) key :source not found in: [file: ...
I don’t really understand why this is necessary, but this should work:
source = "sample.eex"
EEx.function_from_file(
:def, :sample, source, [:assigns], engine: Phoenix.LiveView.HTMLEngine, caller: __ENV__, source: source)
Yes it works. To summarize what to do to use LV 0.18-HTMLEngine as a template engine:
# mix.exs / deps
[
{:jason, "~> 1.4.0"},
{:phoenix_live_view, "~> 0.18"}
]
# config.exs
import Config
# only to make Phoenix happy
config :phoenix, :json_library, Jason
# sample.ex
defmodule Sample do
require EEx
source = "sample.html.heex"
EEx.function_from_file(
:def,
:sample,
source,
[:assigns],
engine: Phoenix.LiveView.HTMLEngine,
caller: __ENV__,
source: source
)
def render(assigns) do
sample(assigns) |> Phoenix.HTML.Safe.to_iodata() |> to_string()
end
def render_to_file(path, assigns) do
# writing to a file does not need to_string
sample(assigns)
|> Phoenix.HTML.Safe.to_iodata()
|> then(fn data -> File.write!(path, data) end)
end
end
# my_component.ex
defmodule MyComponent do
use Phoenix.Component
def greet(assigns) do
~H"""
<p>Hello, <%= assigns.name %></p>
"""
end
end
# sample.html.heex
<MyComponent.greet name={@name} />
# iex(1)> Sample.render(%{name: "World"})
# "<p>Hello, World</p>"
Any reason not to use embed_templates
?
https://hexdocs.pm/phoenix_live_view/0.18.11/Phoenix.Component.html#embed_templates/1
Just when I think: “what a mess, someone should write a macro that automatically loads the templates!”
I just didn’t know about that. Very nice, thank you.
So the whole thing looks like:
defmodule Sample do
use Phoenix.Component
import MyComponent
# sample.html.heex is here
embed_templates("templates/*")
def render(template, assigns, path \\ nil) do
rendered = apply(__MODULE__, template, [assigns]) |> Phoenix.HTML.Safe.to_iodata()
if path, do: File.write!(path, rendered), else: to_string(rendered)
end
end
If someone is coming here to do it the hard way (without embed_templates
, eg you need more control).
This changed with LV 0.18, one now has to:
EEx.function_from_file(
:def,
:fun_name,
source,
[:assigns],
engine: Phoenix.LiveView.Engine,
caller: __ENV__,
source: source
)
warning - not true, this is leex
engine.
Cant get this to work with heex
-engine. Using Phoenix.LiveView.HTMLEngine
will complain that it has no init/1
.
So I now do Phoenix.Template.compile_all
which is a little odd if you want to get a function for exactly one file. But you can still do it:
if you have this template:
/foo/bar/template.html.heex
Phoenix.Template.compile_all(
fn _ -> "template" end,
"/foo/bar/",
"template.html" # do not add .heex here! Its automatically added to the pattern!
)
note, that you can just give this to the engines arg:
%{md: Phoenix.Template.engines().heex, ...}
Now you can read markdown into a template! Cool.
its me again
Got heex 0.18 running with EEx.function_from_file.
EEx.function_from_file(
:def,
:sample,
"sample.html.heex",
[:assigns],
engine: Phoenix.LiveView.TagEngine,
caller: __ENV__,
source: "sample.html.heex",
tag_handler: Phoenix.LiveView.HTMLEngine
)
Dont ask me whats going on, no idea, (and tag_handler
is also not yet documented) I copied the solution from here: Using EEx.eval_string with HEEx Engine on live_view 0.18.18 - #2 by chrismccord