There are already a couple of helperpackages on Github to manage inlining external SVG files into templates which is great from a readability point of view. However I’m guessing because of the way templates are evaluated, EEx tags treated as strings.
Taking the thoughtbot package as the base for a simple example, do something along the lines of:
def inline_svg(file_name) do
path = static_path(file_name)
case File.read(path) do
{:ok, file} -> {:safe, file}
{:error, _} -> raise "No SVG found at #{path}"
end
end
defp static_path(file_name) do
path = "assets/static/svg"
[path, "#{file_name}.svg"] |> Path.join() |> Path.expand
end
That will render the SVG but include the EEx as strings rather than the output of <%= some_func() %> included in the SVG file as expected because of the {:ok, file} -> {:safe, file}. But removing that and going with {:ok, file} -> (file), or wrapping the svg_inline() function with raw() still doesn’t give me the desired outcome.
So I’m wondering where in Phoenix’s order should I be attempting to run this so that tags included in the inlined SVG are rendered as expected?
As far as “pure” EEx is concerned there is absolute no difference, as raw EEx do not care about “tags”, as it do not understands them. If you are talking about Phoenix HTML, then code you have presented should work as expected. Are you sure that returned string is wrapped in tuple?
and for plain old SVG images it works exactly as expected. It’s only when I try to inline files with <%= %>, essentially treating the SVGs as templates that I have a problem.
Following @mindok’s link I seem to have to explicitly call EEx, in this case Phoenix.HTML.Engine and pass it through again but after spending the morning on it I’m not sure if I’m even any closer to an answer.
Your problem is that you’re using a function here. The function will be called at runtime, but at runtime the svg’s won’t be available (when using releases) or at least the path might no longer match. You’d want to use a macro, so you read the svg’s content at compile time and only have the result inlined with all the other eex template contents.
Chatting on IRC I was pointed towards macros as well. Thinking about it, it does make sense it just never crossed my mind to consider the difference between macros & functions.
Writing macros isn’t something I’m familiar with so it’ll probably take me a while to rtfm.
defmacro inline_svg(file_name) do
path = static_path(file_name)
case File.read(path) do
{:ok, file} -> quote do: {:safe, unquote(file)}
{:error, _} -> raise "No SVG found at #{path}"
end
end
Yeah that was one I had tried and linked above. Unfortunately it inlines the SVG file after the template has been evaluated so any EEx tags included appear as strings.
I never worked out a clean way of doing it, and ended up resorting to treating the SVGs template partials and calling them with render/3. I’ll maybe swing back to it at some stage just to make things a little prettier but inlining them as an EEx template does the same job.
I’ve also been keeping an eye on a lib from CoinGaming called Bennu which looks promising, as well as Surface. Those approaches may end up being a better answer.
Cheers Andy. I’m really starting to question myself now and I don’t know if it’s because I’m being really stupid, or I just suck at explaining what I was trying to achieve. Taking your inline macros, much like the ones at the top, I’m still unable to get Phoenix to evaluate EEx tags inside the SVG.
But if you’ve got EEx markup that needs to be compiled into a function to return the SVG, it seems like render with an .svg.eex partial should do the right thing:
render works fine. It just gets a little unwieldy as the number of components increases and they start getting shared between views. Which is why I was saying I’ll maybe get a chance to revisit it at some stage to find a more elegant approach to structuring them in a project e.g. helper macros + a different pattern on use Phoenix.View, root: "".
Using the inline macros for styling elements genuinely wasn’t a use case I’d consider so it makes now why I was confused with responses here