Back

Nesting the rails content_tag helper more deeply

My adventures with nesting the content_tag helper continue. I wrote some code something like the following. Of course, I was using real HTML tags to create a nested list, but to make the example a little more clear, I have substituted tag names that make their place in the hierarchy self evident.

content_tag :level_three do

  concat content_tag :level_two, "item one"

  content_tag :level_two do
    content_tag :level_one, "item two"
  end

end

Once again I was naive to expect output like this:

<level_three>
  <level_two>item one</level_two>
  <level_two>  
    <level_one>item two</level_one>
  </level_two>
</level_three>

because what I actually got was like this:

<level_three>
  <level_two>item one</level_two>
</level_three>

My immediate thought was that the second block needed a concat, so I tried changing the code to this:

content_tag :level_three do

  concat content_tag :level_two, "item one"

  concat content_tag :level_two do
    content_tag :level_one, "item two"
  end

end

That got me a little closer, the output was now:

<level_three>
  <level_two>item one</level_two>
  <level_two></level_two>
</level_three>

All we were missing was the item two block, so we just needed another concat, right? So I added that:

content_tag :level_three do

  concat content_tag :level_two, "item one"

  content_tag :level_two do
    concat content_tag :level_one, "item two"
  end

end

Wrong! The output was unchanged after that addition. WTF!? Well, after a lot of thrashing, this blog post gave me a clue. I can’t claim to understand the details well enough to explain them, but it seems that ActionView is not appending the output correctly in this case, but we can take control of that by putting the offending code in a capture block and appending the output separately. That could look something like this:

content_tag :level_three do

  concat content_tag :level_two, "item one"

  concat(
    capture do
      content_tag :level_two do
        content_tag :level_one, "item two"
      end
    end
  )

end

That code finally produces the desired output:

<level_three>
  <level_two>item one</level_two>
  <level_two>  
    <level_one>item two</level_one>
  </level_two>
</level_three>

This code is illustrative only, please don’t write code that actually looks like that. At a minimum, you should move the capture block into it’s own method to make things more understandable. Something like this:

content_tag :level_three do
  concat content_tag :level_two, "item one"
  concat sub_level
end

def sub_level
  capture do
    content_tag :level_two do
      content_tag :level_one, "item two"
    end
  end
end

One response to “Nesting the rails content_tag helper more deeply”

  1. […] ← Nesting the rails content_tag helper more deeply […]

Leave a Reply

Your email address will not be published. Required fields are marked *