在视图中多个类似的循环,更好的做法?

时间:2012-08-25 15:47:08

标签: ruby-on-rails

使用Rails 3.我的视图有两个选项:

选项A:

<% @items.each do |item| %>
  ...
<% end %>
...

<script>
  var items = [
    <% @items.each do |item| %>
      ['<%= item.name %>', <%= item.lat %>, <%= item.lng %>],
    <% end %>
  ];
</script>

选项B:

<% @items.each do |item| %>
  ...
  <% content_for :items_array do %>
    ['<%= item.name %>', <%= item.lat %>, <%= item.lng %>],
  <% end %>
<% end %>
...

<script>
  var items = [<%= yield :items_array %>];
</script>

目前,我选择了选项B ,但我仍然认为它根本不是很整洁。有更好的方法吗?我拒绝选择 OPTION A ,因为我不希望它在同一视图中循环两次因为性能和DRY问题,如果我错了就纠正我。

感谢。

3 个答案:

答案 0 :(得分:2)

如果是我的项目,我可能会将此问题转移到javascript端。将lat和long放在item标签中的某个数据属性中,并使用不显眼的脚本来读取它们。 (如果你想要一个例子,请告诉我。)

如果那是不可能的,我个人更喜欢选项A,首先是为了可读性(它更容易理解,特别是如果你不熟悉content_for),其次是因为它更容易重构,我们可以移动收集数据在模型上的方法或可能成为帮助器。 DRY并不真正适用于此,因为即使您循环使用相同的数据集,您也会使用它做很多不同的事情。

那就是说,我对性能影响不太确定,所以我进行了一些测试。我在我的一个应用程序中获取了500个客户端记录,并使用选项A呈现链接列表,然后将其数据映射到content_for块并使用Ruby的内置Benchmark模块对其进行基准测试(它是haml而不是erb但我确定它并不难读。)

- bench = Benchmark.measure do
  %ul#client-list
    - @clients.each do |client|
      %li
        = link_to client.company, client
        - content_for :foobar do
          = "['#{j client.name}', '#{j client.company}', '#{j client.email}'],"

  = javascript_tag do
    var nearbys = [#{yield :foobar}];

%p= bench.to_s

结果,大约370毫秒

 0.370000 0.000000 0.370000 ( 0.397476)

然后我做了同样的两个循环(一个each一个map):

- bench = Benchmark.measure do
  %ul#client-list
    - @clients.each do |client|
      %li
        = link_to client.company, client

  = javascript_tag do
    = "var nearbys = [#{ @clients.map { |client| "['#{j client.name}', '#{j client.company}', '#{j client.email}']," }}]"

%p= bench.to_s

结果,大约80毫秒:

0.080000 0.000000 0.080000 ( 0.077940)

您可能希望自己检查结果,但似乎content_for方法不是解决此问题的非常有效的方法。

编辑:仅供参考,使用ree 1.8.7和rails 3.2.6进行测试

EDIT2:

如何在不使用内联javascript的情况下重写此内容的示例。我首先将所有必需的参数移动到data-属性中,例如在视图中:

<% @items.each do |item| %>
  <div class="item" data-name="<%= item.name %>"
                    data-lat="<%= item.lat %>"
                    data-long="<%= item.long %>">
    <%= item.name %>
  </div>
<% end %>

(您也可以从div中获取名称,但将所有内容放在数据属性中会更加一致。)

然后在你的javascript中你可以按如下方式映射数据(假设是jquery):

var nearbys = $('.item').map(function() {
  return [ $(this).data("name"), $(this).data("lat"), $(this).data("long") ];
});

通过这种方式,您可以将所有javascript整齐地保存在单独的文件中,并在客户端收集数据。

答案 1 :(得分:1)

这很简单,只需使用to_json它就可以为你做所有的逃避;)

所以你的代码看起来像这样:

<script>
  var items = <%= @items.map{|o| [o.name, o.lat, o.lng]}.to_json.html_safe %>;
</script>

(html_safe将字符串标记为安全,避免双重转义)

如果你的对象比只是字符串和数字的数组更复杂,你应该使用jQuery的$.parseJSON(json),所以它看起来像:

<script>
  var items = $.parseJSON('<%= @items.map{|o| [o.name, o.lat, o.lng]}.to_json.html_safe %>');
</script>

享受!

答案 2 :(得分:0)

所有这些代码都应该进入Model的帮助文件中的函数。帮助者就是这样,对吧?

视图应该将最少的Ruby代码硬连接到其中。所以,使用助手

以下是一些链接,可让您更深入地了解帮助者: