开发者

In Rails 3.1, how can I create an HTML table generator that uses block style formatting

I'm developing an application that displays tabular data in many different areas and I find myself constantly using the same HTML table structure over and over. For example a particular table looks like this:

%table.zebra-striped#user-table{ :cellspacing => "0" }
  %colgroup
    %col{:id => "email"}
    %col{:id => "username"}
    %col{:id => "sign-in-count"}
    %col{:id => "last-sign-in-at"}

  %thead
 开发者_Go百科   %tr
      %th{:id => "email-head", :scope => "col"} E-mail
      %th{:id => "username-head", :scope => "col"} Username
      %th{:id => "sign-in-count-head", :scope => "col"} Sign Ins
      %th{:id => "last-sign-in-at-head", :scope => "col"} Last Sign In

  %tbody
    - @users.each do |user|
      %tr{ :class => zebra }
        %td
          =h user.email
        %td
          =h user.username
        %td
          =h user.sign_in_count
        %td
          =h user.last_sign_in_at

Ideally, I would like to create some kind of helper method where I could do something like:

= custom_table_for @users do
  = column :email
  = column :username do |user|
    = link_to user.username, user_path(user)
  = column "Sign Ins", :sign_in_count
  = column :last_sign_in_at

This way I can change the formatting of the data in the columns and the column header names if I'm not happy with default values, but have the table generated for me.

I suppose I could create a normal helper, but I'd have to use arrays and I have no idea how I could include custom data formatting per column.

active_admin has something similar to this which you can see here: http://activeadmin.info/docs/3-index-pages/index-as-table.html

Any leads or ideas would be greatly appreciated.


I just came up with this:

A few points:

  • The line @columns = [] is a reset so you can call it more than once.
  • The yield in the custom_table_for calls the block that you pass it.
  • The block in the column method is stored and called in custom_table_for if it is set.

I included a sample class to show the usage too.

please note I did this outside of a rails app and you almost certainly want to use http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag instead of the p "<table>" this is merely for sample purposes when you run it in the console.

module TableHelper
  def custom_table_for(items)
    @columns = []
    yield
    p "<table>"
      @columns.each do |c|
        p "<th>#{c[:value]}</th>"
      end

      items.each do |e|
        p "<tr>"
        @columns.each do |c|
          e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
          p "<td>#{e[c[:name]]}</td>"
        end
        p "</tr>"
      end
    p "</table>"
  end

  def column(name, value = nil, &block)
    value = name unless value
    @columns << {:name => name, :value => value, :block => block}
  end
end

class ExampleTable
  include TableHelper
  def test
    @users = [{:email => "Email 1", :username => "Test User"}, {:email => "Email 2", :username => "Test User 2"}]  
    custom_table_for @users do
      column :email, "Email"
      column :username do |user|
        user.upcase
      end
    end
  end
end

et = ExampleTable.new
et.test

UPDATE

I migrated this to rails to use content_tags

module TableHelper
  def custom_table_for(items)
    @columns = []
    yield


    content_tag :table do
      thead + tbody(items)
    end

  end

  def thead
    content_tag :thead do
      content_tag :tr do 
        @columns.each do |c|
          concat(content_tag(:th, c[:value]))
        end
      end
    end
  end

  def tbody(items)
    content_tag :tbody do
      items.each { |e|
       concat(content_tag(:tr){
          @columns.each { |c|
            e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
            concat(content_tag(:td, e[c[:name]]))
          }
        })
      }
    end
  end

  def column(name, value = nil, &block)
    value = name unless value
    @columns << {:name => name, :value => value, :block => block}
  end
end


To compliment @gazler's response, here's a way to make a table of a single resource-- column one for attribute names, column two for their values:

module TableHelper

  @resource = nil

  def simple_table_for(resource)
    @resource = resource

    content_tag :table do
      content_tag :tbody do
        yield
      end
    end
  end

  def row(key, label = nil, &block)
    if key.is_a? String
      label = key
    end

    content_tag(:tr) {
      concat content_tag :td, label || key.capitalize
      concat content_tag(:td ){
          if block_given?
            yield
          else
            @resource.send(key)
          end
      }
    }
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜