从共享局部视图或替代解决方案中删除条件逻辑

时间:2015-07-16 00:39:25

标签: ruby-on-rails ruby partials

对于当前项目,我在视图之间有重复的代码,而且我不确定重构它的最佳路径。

我似乎处于可以在各种.html.erb文件中包含重复代码的位置,或者我可以将相同的代码放入部分和使用条件中。我总是听到逻辑应该不在意见之列。这两种选择似乎都不理想,而且我目前还不知道其他选择。

为了说明我的问题,我创建了一个名为animals的简单rails应用程序。我为两个模型搭建了一个模型:一个用于cat,另一个用于dog。图像显示其相应的属性: enter image description here Dogs

显示@cats@dogs几乎相同。 Cats只有meows的列,而Dogsbarks的列,而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 %>

显然,对于这个具体的例子来说做这样的事情会有点过头了,但我应用它的现实世界项目不会有点矫枉过正。

总的问题是:如何删除迭代集合的几乎相同的代码,唯一的区别是添加/删除一列信息?把这种逻辑放在视图本身中是不对的,并且让复制感觉不对。

5 个答案:

答案 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>