Loop & output content_tags within content_tag in helper
Solution 1
Try this:
def foo_list items
content_tag :ul do
items.collect {|item| concat(content_tag(:li, item))}
end
end
Solution 2
I couldn't get that work any better.
If you were using HAML already, you could write your helper like this:
def foo_list(items)
haml_tag :ul do
items.each do |item|
haml_tag :li, item
end
end
end
Usage from view:
- foo_list(["item_one", link_to("item_two", "#"), ... ])
Output would be correctly intended.
Solution 3
You could use content_tag_for
, which works with collections:
def foo_list(items)
content_tag(:ul) { content_tag_for :li, items }
end
Update: In Rails 5 content_tag_for
(and div_for
) were moved into a separate gem. You have to install the record_tag_helper
gem in order to use them.
Solution 4
Along with answers above, this worked for me well:
(1..14).to_a.each do |age|
concat content_tag :li, "#{link_to age, '#'}".html_safe
end
Solution 5
The big issue is that content_tag isn't doing anything smart when it receives arrays, you need to send it already processed content. I've found that a good way to do this is to fold/reduce your array to concat it all together.
For example, your first and third example can use the following instead of your items.map/collect line:
items.reduce(''.html_safe) { |x, item| x << content_tag(:li, item) }
For reference, here is the definition of concat that you're running into when you execute this code (from actionpack/lib/action_view/helpers/tag_helper.rb).
def concat(value)
if dirty? || value.html_safe?
super(value)
else
super(ERB::Util.h(value))
end
end
alias << concat
Comments
-
DEfusion over 3 years
I'm trying a helper method that will output a list of items, to be called like so:
foo_list( ['item_one', link_to( 'item_two', '#' ) ... ] )
I have written the helper like so after reading Using helpers in rails 3 to output html:
def foo_list items content_tag :ul do items.collect {|item| content_tag(:li, item)} end end
However I just get an empty UL in that case, if I do this as a test:
def foo_list items content_tag :ul do content_tag(:li, 'foo') end end
I get the UL & LI as expected.
I've tried swapping it around a bit doing:
def foo_list items contents = items.map {|item| content_tag(:li, item)} content_tag( :ul, contents ) end
In that case I get the whole list but the LI tags are html escaped (even though the strings are HTML safe). Doing
content_tag(:ul, contents.join("\n").html_safe )
works but it feels wrong to me and I feelcontent_tag
should work in block mode with a collection somehow. -
DEfusion over 13 yearsThat works, I'd tried doing the concat around the collect, previously rather than within. With concat inside the proc then collect isn't needed and you can use a items.each (or other iterators).
-
Ivailo Bardarov about 13 yearsThis way you are calling concat on each element. I think that it would be cheaper to do items.collect{}.join("").html_safe
-
nruth about 8 yearsMay need to change <%= to <% in the template when doing this.
-
hellion over 7 yearscontent_tag_for removed in Rails 5
-
Tallboy about 6 yearsCan you explain how concat is working here? I thought concat has to be called on another object?
-
zetetic about 6 years@Tallboy
concat
is explained here: api.rubyonrails.org/classes/ActionView/Helpers/…. The method is being called on the view context (I think). You may be confusing it withString#concat
-
Tallboy about 6 yearsThank you, I couldnt find that method before. That explains it