对于当前项目,我在视图之间有重复的代码,而且我不确定重构它的最佳路径。
我似乎处于可以在各种.html.erb
文件中包含重复代码的位置,或者我可以将相同的代码放入部分和使用条件中。我总是听到逻辑应该不在意见之列。这两种选择似乎都不理想,而且我目前还不知道其他选择。
为了说明我的问题,我创建了一个名为animals
的简单rails应用程序。我为两个模型搭建了一个模型:一个用于cat
,另一个用于dog
。图像显示其相应的属性:
显示@cats
和@dogs
几乎相同。 Cats
只有meows
的列,而Dogs
有barks
的列,而dog
的附加属性列为plays_catch
。
假设我们选择通过将共享视图设为部分来减少显示猫狗的重复代码:
#views/shared/_animal.html.erb
<tr>
<td><%= animal.name %></td>
<td><%= animal.age %> </td>
<% if animal.class == Cat %>
<td><%= animal.meows %> </td>
<% end %>
<% if animal.class == Dog %>
<td><%= animal.barks %> </td>
<td><%= animal.plays_catch %> </td>
<% end %>
</tr>
然后渲染@cats = Cat.all
:
<%= render partial: "shared/animal", collection: @cats %>
然后渲染@dogs = Dog.all
:
<%= render partial: "shared/animal", collection: @dogs %>
显然,对于这个具体的例子来说做这样的事情会有点过头了,但我应用它的现实世界项目不会有点矫枉过正。
总的问题是:如何删除迭代集合的几乎相同的代码,唯一的区别是添加/删除一列信息?把这种逻辑放在视图本身中是不对的,并且让复制感觉不对。
答案 0 :(得分:2)
您可以使用装饰器并添加返回额外列的方法:
class DogDecorator < Draper::Decorator
def extra_columns
[:barks, plays_catch]
end
end
class CatDecorator < Draper::Decorator
def extra_columns
[:meows]
end
end
...
<% animal.extra_columns.each do |column| %>
<td><%= animal.attributes[column.to_s] %>
<% end %>
...
<% @cats = CatDecorator.decorate_collection(Cat.all)
<%= render partial: "shared/animal", collection: @cats %>
答案 1 :(得分:1)
您可以使用respond_to?
更一般地解决问题。当视图逻辑更通用时,它看起来并不是错误的。
<% [:meows, :barks, :plays_catch].each do |method| %>
<% if animal.respond_to?(method) %>
<td><%= animal.send(method) %> </td>
<% end %>
<% end %>
答案 2 :(得分:1)
我不知道有任何规范的方法来完成此任务,但我会以下列方式使用partial
:
<tr>
<% animal.attributes.each do |_, value| %>
<td><%= value %></td>
<% end %>
</tr>
通过在局部变量中提供预先获得的模型属性,可以摆脱重复的attributes
调用。
编辑 :如果您只想显示某些属性。
# Declare whitelist of attributes
# (you can also declare a blacklist and just calculate the difference between two array: all_attributes - blacklist_attributes):
<% whitelist = [:name, :age, :barks] %>
<%= render partial: 'shared/animal',
collection: @dogs,
locals: {attrs: (@dogs.first.attributes.keys.map(&:to_sym) & whitelist)} %>
视图/共享/ _animal.html.erb :
<tr>
<% attrs.each do |attr| %>
<td><%= animal[attr] %></td>
<% end %>
</tr>
答案 3 :(得分:1)
您可以为Cat和Dog类添加同名的方法,这将返回特定的实例属性名称和值。我建议返回两个数组(一个包含字段的名称,另一个包含字段的值,反之亦然)因为哈希不是精确排序的。这样您就可以控制它们在视图中的显示顺序。 例如:
#models/cat.rb
def fields_and_attributes
fields = ["Name","Age","Meows"]
attributes = [self.name, self.age]
if self.meows
attributes.push("Yes")
else
attributes.push("No")
end
[fields,attributes] # make sure each attribute is positioned in the same index of its corresponding field
end
#models/dog.rb
def fields_and_attributes
fields = ["Name","Age","Plays catch"]
attributes = [self.name, self.age]
if self.plays_catch
attributes.push("Yes")
else
attributes.push("No")
end
[fields,attributes] # make sure each attribute is positioned in the same index of its corresponding field
end
#controllers/animals_controller.rb
def display_animals
@animals = Cat.all + Dog.all # an array containing the different animals
end
#views/display_animals.html.erb
for i in (0...@animals.size)
fields_and_attributes = @animals[i].fields_and_attributes
for f in (0...fields_and_attributes[0].size)
<p><%= fields_and_attributes[0][f] %> : <%= fields_and_attributes[1][f] %></p>
end
end
在这里,我们首先遍历所有动物并调用该特定记录的.fields_and_attributes
方法;然后我们迭代调用该方法的结果,以与方法中定义的顺序相同的顺序显示字段和属性,并且还保证代码将显示每个字段和每个属性,而不管字段总数的差异如何每种不同的动物。
答案 4 :(得分:0)
在回顾发布的答案后,我的回答如下。基本上是:
以下代码:
#app/views/cats/index.html.erb
<h1>Listing Cats</h1>
<table>
<thead>
<tr>
<%= render partial: "shared/cat_dog_table_headers" %>
<th>Meows</th>
</tr>
</thead>
<tbody>
<% @cats.each do |cat| %>
<tr>
<%= render partial: "shared/cat_dog_table_data", locals: {animal: cat} %>
<td><%= cat.meows %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Cat', new_cat_path %>
对于狗:
#app/views/dogs/index.html.erb
<h1>Listing Dogs</h1>
<table>
<thead>
<tr>
<%= render partial: "shared/cat_dog_table_headers" %>
<th>Barks</th>
<th>Plays catch</th>
</tr>
</thead>
<tbody>
<% @dogs.each do |dog| %>
<tr>
<%= render partial: "shared/cat_dog_table_data", locals: {animal: dog} %>
<td><%= dog.barks %></td>
<td><%= dog.plays_catch %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Dog', new_dog_path %>
猫和狗的共享表格标题:
#app/views/shared/_cat_dog_table_headers
<td><%= Name %></td>
<td><%= Age %></td>
猫和狗的共享表数据:
#app/views/shared/_cat_dog_table_data_headers
<td><%= animal.name %></td>
<td><%= animal.age %></td>