I tried my module generation code somewhere else (outside the liveview), and it’s working.
Why is that?
What I’m trying to do is to generate my own liveviews dynamically at runtime.
Here is the full code of my generator:
def gen(page) do
options = [
engine: Phoenix.LiveView.TagEngine,
caller: __ENV__,
indentation: 2,
file: "common_view.html.heex",
line: 1,
source: "common_view.html.heex",
tag_handler: Phoenix.LiveView.HTMLEngine
]
r = EEx.compile_file("lib/my_project_web/live/test_renderer_live/common_view.html.heex", options)
mod_name = Module.concat(MyProjectWeb.TestRenderertLive.Pages, page)
Module.create(
mod_name,
quote do
def my_render(assigns) do
unquote(r)
end
end,
line: 1,
file: "no_file.exs"
)
end
The idea is to call this my_render
function in a generic LiveView using render_with
.
The compilation of the HEEx template is working fine. I tried to execute Code.eval_quoted
with the compiled template code and it’s working (it’s returning the expected Phoenix.LiveView.Rendered
struct).
But when I try to set this compiled template inside my generated my_render
function, I have another CompileError
:
error: undefined variable "assigns"
└─ no_file.exs:1: MyProjectWeb.TestRenderertLive.Pages.TestPage.my_render/1
But assigns
exists. It’s in the my_render
function arguments.
The compiled template returns an AST in which the assigns
variable is used this way (AST):
{:assigns, [], nil}
Is there something I can do to make my assigns
variable visible to the compiled template?
I know my use case could be done a different way using eval_quoted
in a “normal” render
function, like this:
def handle_params(%{"page" => page}, _url, socket) do
options = [
engine: Phoenix.LiveView.TagEngine,
caller: __ENV__,
indentation: 2,
file: "#{page}.html.heex",
line: 1,
source: "#{page}.html.heex",
tag_handler: Phoenix.LiveView.HTMLEngine
]
r = EEx.compile_file("lib/my_project_web/live/test_renderer_live/#{page}.html.heex", options)
{:noreply,
socket
|> assign(:page, page)
|> assign(:renderer, r)}
end
def render(assigns) do
{res, _bindings} =
Code.eval_quoted(assigns.renderer, [assigns: assigns],
line: 1,
file: "#{assigns.page}.html.heex"
)
res
end
But I’m trying the compiled way to optimize speed. I’d like to benchmark the 2 approches.
So I’d like to be able to compile a custom render function that contains the compiled template, and use it directly when the page is asked, instead of compiling the template at every page load.