Цикл и вывод content_tags внутри content_tag в помощнике

Я пытаюсь использовать вспомогательный метод, который будет выводить список элементов, который будет называться так:

foo_list( ['item_one', link_to( 'item_two', '#' ) ... ] )

Я написал помощник таким образом после прочтения Использование помощников в рельсах 3 для вывода HTML:

def foo_list items
    content_tag :ul do
        items.collect {|item| content_tag(:li, item)}
    end
end

Однако в этом случае я просто получаю пустой UL, если делаю это в качестве теста:

def foo_list items
    content_tag :ul do
        content_tag(:li, 'foo')
    end
end

Я получаю UL & LI, как и ожидалось.

Я попытался немного поменять местами:

def foo_list items
    contents = items.map {|item| content_tag(:li, item)}
    content_tag( :ul, contents )
end

В этом случае я получаю весь список, но теги LI экранированы html (даже если строки безопасны для HTML). Выполнение content_tag(:ul, contents.join("\n").html_safe ) работает, но мне кажется, что это неправильно, и я чувствую, что content_tag должен каким-то образом работать в блочном режиме с коллекцией.


person DEfusion    schedule 12.01.2011    source источник


Ответы (5)


Попробуй это:

def foo_list items
  content_tag :ul do
      items.collect {|item| concat(content_tag(:li, item))}
  end
end
person zetetic    schedule 12.01.2011
comment
Это работает, я пытался выполнить concat вокруг сбора, раньше, а не внутри. С concat внутри proc тогда сбор не нужен, и вы можете использовать items.each (или другие итераторы). - person DEfusion; 13.01.2011
comment
Таким образом, вы вызываете concat для каждого элемента. Я думаю, что было бы дешевле сделать items.collect{}.join().html_safe - person Ivailo Bardarov; 13.03.2011
comment
Можете ли вы объяснить, как здесь работает concat? Я думал, что concat нужно вызывать для другого объекта? - person Tallboy; 19.04.2018
comment
@Tallboy concat объясняется здесь: api.rubyonrails.org /классы/ActionView/Помощники/. Метод вызывается в контексте представления (я думаю). Вы можете спутать его с String#concat - person zetetic; 19.04.2018
comment
Спасибо, я не мог найти этот метод раньше. Что объясняет его - person Tallboy; 19.04.2018

Я не мог получить эту работу лучше.

Если вы уже использовали HAML, вы можете написать свой помощник следующим образом:

def foo_list(items)
  haml_tag :ul do
    items.each do |item|
      haml_tag :li, item
    end
  end
end

Использование из представления:

- foo_list(["item_one", link_to("item_two", "#"), ... ])

Вывод будет правильно предназначен.

person Heikki    schedule 12.01.2011

Вы можете использовать content_tag_for, который работает с коллекциями. :

def foo_list(items)
  content_tag(:ul) { content_tag_for :li, items }
end

Обновление: в Rails 5 content_tag_fordiv_for) были перемещены в отдельный гем. Вы должны установить гем record_tag_helper, чтобы использовать их.

person Daniel Rikowski    schedule 02.07.2013
comment
content_tag_for удален в Rails 5 - person hellion; 22.09.2016

Наряду с ответами выше, это сработало для меня хорошо:

(1..14).to_a.each do |age|
  concat content_tag :li, "#{link_to age, '#'}".html_safe
end
person The Whiz of Oz    schedule 09.06.2015
comment
При этом может потребоваться изменить ‹%= на ‹% в шаблоне. - person nruth; 17.02.2016

Большая проблема заключается в том, что content_tag не делает ничего умного, когда получает массивы, вам нужно отправить уже обработанный контент. Я обнаружил, что хороший способ сделать это - свернуть/уменьшить ваш массив, чтобы объединить его все вместе.

Например, в вашем первом и третьем примере вместо строки items.map/collect можно использовать следующее:

items.reduce(''.html_safe) { |x, item| x << content_tag(:li, item) }

Для справки, вот определение concat, с которым вы сталкиваетесь при выполнении этого кода (из 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
person billmag    schedule 28.11.2011