在一个 ERB 块中渲染多个表达式的输出
Render output of multiple expressions in one ERB block
我有一个像这样的助手:
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
link_to vote_inspired_post_path(post, vote_scope: :inspired), method: :post, data: { confirm: 'Are you sure this post Inspires you?' }, class: "btn btn-default" do
"<i class='fa fa-lightbulb-o'></i> <br />Inspired".html_safe
end
link_to vote_happy_post_path(post, vote_scope: :happy), method: :post, data: { confirm: 'Are you sure this post makes you happy?' }, class: "btn btn-success" do
"<i class='fa fa-smile-o'></i> <br />Happy".html_safe
end
link_to vote_disappointed_post_path(post, vote_scope: :disappointed), method: :post, data: { confirm: 'Are you sure this post disappointed you?' }, class: "btn btn-info" do
"<i class='fa fa-meh-o'></i> <br />Disappointed".html_safe
end
link_to vote_upset_post_path(post, vote_scope: :upset), method: :post, data: { confirm: 'Are you sure this post upsets you?' }, class: "btn btn-inverse" do
"<i class='fa fa-frown-o'></i> <br />Upset".html_safe
end
end
我需要呈现所有链接及其嵌套的 <i>
标签 - 但出于某种原因,此版本只呈现最后一行。
所有这些都在一个名为 show_vote_buttons(post)
的方法中,在视图中是这样调用的:<%= show_vote_buttons(@post) %>
解决这个问题的最佳方法是什么?
基本上,这背后的原因是 <%= %>
渲染了 show_vote_buttons
方法的输出。此方法没有明确 return 任何内容,因此最后计算的表达式是 returned,在您的情况下,是最后的 link_to
输出。
在一般情况下,如果您没有使用辅助方法而只是粘贴它的主体而不是多次 link_to
调用,您会得到相同的行为。原因类似:<%= %>
不渲染每个 link_to
,它在 <%= %>
中执行代码,然后输出最后一个计算表达式的结果。
我看到两种不同的方法来改变输出:
- 辅助方法:连接所有求值表达式的输出并打印为一个字符串:
1.1 在每个 link_to
;
周围使用 <<
和圆括号 ()
1.2 创建一个带双引号 "
的字符串,并在每个 link_to
输出中插入 #{}
;
1.3 使用 concat
;
- 部分视图:从现有的辅助方法构建一个单独的视图,并使用
<%= %>
输出每个link_to
。
P.S。在测试了所有四种方法之后,我得出一个结论(这里是个人意见),第二种方法更可取,至少因为它在视图中保持渲染并避免看起来混乱的串联。例如,在 Devise gem 中使用了类似的方法,其中所有共享链接都位于 app/views/devise/shared/_links.html.erb
部分。
@suslov 围绕这个答案完美地解释了原因。元编程可能会使代码保持干燥:
DATA = {
inspired: { ico: 'fa-lightbulb-o',
msg: 'Are you sure this post Inspires you?',
btn: 'btn-default' },
happy: { ico: 'fa-smile-o',
msg: 'Are you sure this post makes you happy?',
btn: 'btn-success' },
... }
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
DATA.map do |k, v|
instance_eval %Q{
link_to vote_#{k}_post_path(post, vote_scope: :#{k}),
method: :post,
data: { confirm: #{v[:msg]} },
class: "btn #{v[:btn]}" do
"<i class='fa #{v[:ico]'></i> <br />#{k.capitalize}".html_safe
end
}.join($/) # return them all, concatenated with separator
end
end
but for some reason, this version is just rendering the last line.
那个原因是因为在Ruby中,如果一个方法没有明确的return
,那么方法的returned值是最后一行的值在方法退出之前执行。如果你想 return 来自一个方法的多个值,请使用 return
关键字并用逗号分隔你的值。
#will return an array [1,2,3]
def some_method
return 1,2,3
end
此外,我同意其他答案,即您的代码应该更 DRY,但我只是想澄清您最初的问题。
试试这个代码
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
concat(link_to vote_inspired_post_path(post, vote_scope: :inspired), method: :post, data: { confirm: 'Are you sure this post Inspires you?' }, class: "btn btn-default" do
safe_concat(content_tag(:i, "Inspired", calss: "fa fa-lightbulb-o"))
end)
concat(link_to vote_happy_post_path(post, vote_scope: :happy), method: :post, data: { confirm: 'Are you sure this post makes you happy?' }, class: "btn btn-success" do
safe_concat(content_tag(:i, "Happy", calss: "fa fa-smile-o"))
end)
concat(link_to vote_disappointed_post_path(post, vote_scope: :disappointed), method: :post, data: { confirm: 'Are you sure this post disappointed you?' }, class: "btn btn-info" do
safe_concat(content_tag(:i, "Disappointed", calss: "fa fa-meh-o"))
end)
concat(link_to vote_upset_post_path(post, vote_scope: :upset), method: :post, data: { confirm: 'Are you sure this post upsets you?' }, class: "btn btn-inverse" do
safe_concat(content_tag(:i, "Upset", calss: "fa fa-frown-o"))
end)
end
注:没测试过
我有一个像这样的助手:
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
link_to vote_inspired_post_path(post, vote_scope: :inspired), method: :post, data: { confirm: 'Are you sure this post Inspires you?' }, class: "btn btn-default" do
"<i class='fa fa-lightbulb-o'></i> <br />Inspired".html_safe
end
link_to vote_happy_post_path(post, vote_scope: :happy), method: :post, data: { confirm: 'Are you sure this post makes you happy?' }, class: "btn btn-success" do
"<i class='fa fa-smile-o'></i> <br />Happy".html_safe
end
link_to vote_disappointed_post_path(post, vote_scope: :disappointed), method: :post, data: { confirm: 'Are you sure this post disappointed you?' }, class: "btn btn-info" do
"<i class='fa fa-meh-o'></i> <br />Disappointed".html_safe
end
link_to vote_upset_post_path(post, vote_scope: :upset), method: :post, data: { confirm: 'Are you sure this post upsets you?' }, class: "btn btn-inverse" do
"<i class='fa fa-frown-o'></i> <br />Upset".html_safe
end
end
我需要呈现所有链接及其嵌套的 <i>
标签 - 但出于某种原因,此版本只呈现最后一行。
所有这些都在一个名为 show_vote_buttons(post)
的方法中,在视图中是这样调用的:<%= show_vote_buttons(@post) %>
解决这个问题的最佳方法是什么?
基本上,这背后的原因是 <%= %>
渲染了 show_vote_buttons
方法的输出。此方法没有明确 return 任何内容,因此最后计算的表达式是 returned,在您的情况下,是最后的 link_to
输出。
在一般情况下,如果您没有使用辅助方法而只是粘贴它的主体而不是多次 link_to
调用,您会得到相同的行为。原因类似:<%= %>
不渲染每个 link_to
,它在 <%= %>
中执行代码,然后输出最后一个计算表达式的结果。
我看到两种不同的方法来改变输出:
- 辅助方法:连接所有求值表达式的输出并打印为一个字符串:
1.1 在每个link_to
;
周围使用<<
和圆括号()
1.2 创建一个带双引号"
的字符串,并在每个link_to
输出中插入#{}
;
1.3 使用concat
; - 部分视图:从现有的辅助方法构建一个单独的视图,并使用
<%= %>
输出每个link_to
。
P.S。在测试了所有四种方法之后,我得出一个结论(这里是个人意见),第二种方法更可取,至少因为它在视图中保持渲染并避免看起来混乱的串联。例如,在 Devise gem 中使用了类似的方法,其中所有共享链接都位于 app/views/devise/shared/_links.html.erb
部分。
@suslov 围绕这个答案完美地解释了原因。元编程可能会使代码保持干燥:
DATA = {
inspired: { ico: 'fa-lightbulb-o',
msg: 'Are you sure this post Inspires you?',
btn: 'btn-default' },
happy: { ico: 'fa-smile-o',
msg: 'Are you sure this post makes you happy?',
btn: 'btn-success' },
... }
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
DATA.map do |k, v|
instance_eval %Q{
link_to vote_#{k}_post_path(post, vote_scope: :#{k}),
method: :post,
data: { confirm: #{v[:msg]} },
class: "btn #{v[:btn]}" do
"<i class='fa #{v[:ico]'></i> <br />#{k.capitalize}".html_safe
end
}.join($/) # return them all, concatenated with separator
end
end
but for some reason, this version is just rendering the last line.
那个原因是因为在Ruby中,如果一个方法没有明确的return
,那么方法的returned值是最后一行的值在方法退出之前执行。如果你想 return 来自一个方法的多个值,请使用 return
关键字并用逗号分隔你的值。
#will return an array [1,2,3]
def some_method
return 1,2,3
end
此外,我同意其他答案,即您的代码应该更 DRY,但我只是想澄清您最初的问题。
试试这个代码
if current_user.find_voted_items(vote_scope: :inspired).include?(post)
concat(link_to vote_inspired_post_path(post, vote_scope: :inspired), method: :post, data: { confirm: 'Are you sure this post Inspires you?' }, class: "btn btn-default" do
safe_concat(content_tag(:i, "Inspired", calss: "fa fa-lightbulb-o"))
end)
concat(link_to vote_happy_post_path(post, vote_scope: :happy), method: :post, data: { confirm: 'Are you sure this post makes you happy?' }, class: "btn btn-success" do
safe_concat(content_tag(:i, "Happy", calss: "fa fa-smile-o"))
end)
concat(link_to vote_disappointed_post_path(post, vote_scope: :disappointed), method: :post, data: { confirm: 'Are you sure this post disappointed you?' }, class: "btn btn-info" do
safe_concat(content_tag(:i, "Disappointed", calss: "fa fa-meh-o"))
end)
concat(link_to vote_upset_post_path(post, vote_scope: :upset), method: :post, data: { confirm: 'Are you sure this post upsets you?' }, class: "btn btn-inverse" do
safe_concat(content_tag(:i, "Upset", calss: "fa fa-frown-o"))
end)
end
注:没测试过