Paulo Janeiro
Paulo Janeiro

Reputation: 3211

Using Elixir inside string interpolation is not working properly in my template

I'm rendering a template and passing some options to it. In one of them I have this code:

main: "#{ @comp[:contentItem]
          unless Regex.match?(~r/width:/, to_string @comp[:contentItem])
                 do "width: 165px;" end
          unless Regex.match?(~r/height:/, to_string @comp[:contentItem])
                 do "height:65px;" end
        }"

I found that only the second code inside unless executes and that if I copy the first unless and repeat it again after the second unless it works, so there seems to be a problem running the code immediately after @comp[:contentItem].
I've tried using () and other combinations with no luck.

Upvotes: 0

Views: 709

Answers (1)

Dogbert
Dogbert

Reputation: 222118

If you add multiple code blocks in a string interpolation like this, all except the last one are ignored. Elixir also shows a warning for this (maybe eex doesn't):

iex(1)> "#{1
...(1)> 2
...(1)> 3}"
warning: code block contains unused literal 1 (remove the literal or assign it to _ to avoid warnings)
  iex:1

warning: code block contains unused literal 2 (remove the literal or assign it to _ to avoid warnings)
  iex:1

"3"

The fix is to use 3 separate interpolations. For your code, this should work:

main: "#{@comp[:contentItem]}\
#{unless Regex.match?(~r/width:/, to_string @comp[:contentItem]) do "width: 165px;" end}\
#{unless Regex.match?(~r/height:/, to_string @comp[:contentItem]) do "height:65px;" end}"

I'm using \ at the end to make sure there's no extra space(s) inserted between the 3 expressions.

Edit: As @cdegroot pointed out, adding such complex logic to templates is considered a terrible practice. You should do this computation in the relevant view, controller, model, or a separate module. It's considered a best practice to have as simple templates as possible, usually just loops and printing out fields of assigns/maps/structs.

Upvotes: 3

Related Questions