AlchemistCamp
How to replace render_existing/3 with function_exported?/3
I have several apps with something like the following in the app.html.eex or app.html.heex:
<%= render_existing(view_module(@conn), "_meta." <> view_template(@conn), assigns)
|| render AppWeb.LayoutView, "_meta.html", assigns %>
This setup renders the metadata for a given view “foo.html” by looking for “_meta.foo.html” and rendering it if it exists. I use the meta templates for holding SEO-related meta, OG meta, and various other tags that go into the tag of a document.
If meta doesn’t exist, then the logic above renders a default template called layout/_meta.html. as the default source.
After upgrading Phoenix, I’m now seeing a compilation warning telling me to use function_exported?/3 instead of render_existing/3.
warning: Phoenix.View.render_existing/3 is deprecated. Use function_exported?/3 instead
lib/app_web/templates/layout/app.html.eex:2: AppWeb.LayoutView."app.html"/1
What would be a good way of following this advice (without breaking the behavior of 100+ _meta templates)? There’s a considerable gap in the interface of the suggested replacement function.
Most Liked
AlchemistCamp
I gave it my best shot this morning during a livestream before work and unfortunately, just couldn’t get a working solution.
Without render_existing/3, we can no longer determine whether a template exists. Depending on deployment strategies, the filesystem is sometimes no longer available.
Unfortunately, function_exported?/3 doesn’t fill the gap since render/3 will always be exported.
This means the only options would be
- writing and maintaining hundreds and hundreds of meta functions throughout the application
- crawling the filesystem at compile time to create a list of all existing template names
- using try and rescue on every page load
- some kind of macro wizardry I haven’t yet been able to think of
LostKobrakai
The idea is moving to function components over templates. Function components are a lot more flexible than templates and given they’re based on (differently named) functions there’s less phoenix magic needed – magic the phoenix teams seems to like to get away from. But not everybody will jump on board of restructing their projects immediately, therefore I feel the deprecation was a bit premature.
You’d replace your code with something like this:
<%= if function_exported?(view_module(@conn), :meta 1), do: view_module(@conn).meta(Map.merge(assigns, %{template: view_template(@conn)}), else: meta(assigns) %>
# LayoutView
def meta(assigns) do
~H"""
…
"""
end
# OtherView
def meta(%{template: "index.html"} = assigns) do
~H"""
…
"""
end
…
From those meta functions you can also call your existing templates using:
render("_meta.#{assigns.template}", assigns)
AlchemistCamp
I looked at the docs before creating this thread. It’s because the examples provided weren’t applicable to my use case that I asked here in the first place.
I’ve found that _meta.foo.html files for simpler cases and defining a render("_meta.foo.html", assigns) functions in the view for more complex cases a very useful pattern.
AlchemistCamp
Oh, not at all! Your comment was fine.
I was just clarifying that I had already looked through the suggestions and hadn’t seen a way to apply them to this use case.
I also wonder the exact same thing you asked. Was there a usage the team was trying to prevent by deprecating the function? Or was it maybe for some more general optimization in Phoenix?
I’m curious.
msimonborg
Perhaps one approach would be to define a global view helper render_meta/2
def render_meta(conn, assigns) do
try do
render(view_module(conn), "_meta." <> view_template(conn), assigns)
rescue
Phoenix.Template.UndefinedError ->
render(AppWeb.LayoutView, "_meta.html", assigns)
end
end
and call it in your templates with render_meta(@conn, assigns)
Also I’m not 100% sure, but I think you can remove the module name view_module(@conn) from your render call when you’re referencing the view that you’re calling from.
EDIT: There are some examples on using function_exported?/3 in the docs if you want to follow the compiler warning’s suggestion, but seems like it might require you to rewrite your meta templates into functions







