cards_for
效果我需要用 BootStrap Card 组件实现一个卡片列表,单个的 BootStrap Card 组件的使用方式如下:
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
</div>
这是它的效果:
现在我需要展示一堆这样的卡片,卡片之间要有间距,大致是这样的效果(这是一行的效果,实际上应当支持多行,这里不再展示):
就此机会,我的第一步是实现一个名为 card 的局部视图,像这样:
<!-- _card.html.erb -->
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title"><%= name %></h5>
<p class="card-text"><%= description %></p>
<a href="<%= link %>" class="btn btn-primary">Go somewhere</a>
</div>
</div>
card 局部视图接受三个变量:name
、description
、link
,分别显示卡片的标题,正文和指向的链接。
如果需要显示一堆卡片,我需要这样做:
<div class="d-flex gap-3 p-3 flex-wrap">
<% @models.each do |model| %>
<%= render "card", name: model.name,
description: model.description,
link: model.link %>
<% end %>
</div>
然后我想到我的项目中有一堆这样模型,它也是满足 name
、description
、link
这三个属性的。为了减少重复代码,我决定实现一个自定义的 helper 方法,来简化这些卡片列表的渲染过程。这不仅能提高代码的可读性,还能确保一致的样式和结构。
我将这个方法命名为 cards_for
,就像 Rails 自带的视图帮助方法 form_for
等一样,cards_for
方法只接受一个满足要求的 models
属性即可:
<%= cards_for models: @models %>
在 Helper 中我只需要实现成这样:
module CardsHelper
def cards_for(models:)
cards_html = models.map do |model|
render "card", name: model.name,
description: model.description,
link: model.link
end.join.html_safe
content_tag :div, cards_html, class: "d-flex gap-3 p-3 flex-wrap"
end
end
在 Helper 中写代码,简直就像在视图中写代码那样,只要你找到对应的方法。
cards_for
添砖加瓦所受的伤接下来,我的任务是继续为 cards_for
方法添砖加瓦。因为我们总不能保证传递进来的模型满足 name
、description
、link
这样的命名关系,为了适应这种情况,我的初步想法是,为 cards_for 方法添加一个块,将块的返回值作为局部变量传递给 card 视图:
module CardsHelper
def cards_for(models:, &block)
cards_html = models.map do |model|
if block_given?
options = block.call(model)
else
options = {
name: model.name,
description: model.description,
link: model.link
}
end
render partial: "card", locals: options # 保险起见,我写成这样
end.join.html_safe
content_tag :div, cards_html, class: "d-flex gap-3 p-3 flex-wrap"
end
end
然后我们就可以针对模型显示一些不一样的东西,例如:
<%= cards_for models: @models do |model| %>
{
name: "T-" + model.name,
description: "T-" + model.description,
link: "https://www.sina.com"
}
<% end %>
然后给我报了这样一个错误:
undefined method `keys' for "\n {\n name: \"T-\" + model.name,\n description: \"T-\" + model.description,\n link: \"https://www.sina.com\"\n }\n":String
原因是我调用的写法写错了,要改成这样才正确:
<%= cards_for models: @models do |model|
{
name: "T-" + model.name,
description: "T-" + model.description,
link: "https://www.sina.com"
}
end %>
注意区别很有限,只比上一个少了一个 %>
结束标签和 <%
打开标签,但也是我们经常犯的错误。原因是因为 ERB 对于不在 <% ... %>
标签内的代码,它会视为纯文本。请细细品味,方能知晓。
至此,一个 cards_for
帮助方法已经实现了。还可以继续为其添砖加瓦,利用 Ruby 语言的灵活性。