module ExpressTemplates module Components # Create an html table from a collection of data. # # Typically this will be a collection of models # of the same type. Each member of the collection must # respond to the column names provided. # # Example: # # ```ruby # table_for(:people) do |t| # t.column :name # t.column :email # t.column :phone # end # ``` # # This assumes that a @people variable will exist in the # view and that it will be a collection whose members respond to # :name, :email, and :phone # # This will result in markup like the following: # # <table id="people"> # <thead> # <tr> # <th class="name">Name</th> # <th class="email">Email</th> # <th class="phone">Phone</th> # </tr> # </thead> # <tbody> # <tr id="person-1"> # <td class="name">Steven Talcott Smith</td> # <td class="email">steve@aelogica.com</td> # <td class="phone">415-555-1212</td> # </tr> # </tbody> # </table> # class TableFor < Base include Capabilities::Configurable include Capabilities::Building def initialize(*args) super(*args) _process_args!(args) # from Configurable yield(self) if block_given? end attr :columns def column(name, options = {}) @columns ||= [] @columns << Column.new(name, options) end emits -> { table(my[:id]) { thead { tr { columns.each do |column| th.send(column.name) { column.title } end } } tbody { for_each(my[:id]) { tr(id: -> {"item-#{item.id}"}, class: my[:id].to_s.singularize) { columns.each do |column| td(class: column.name) { column.format(:item) } end } } } } } def wrap_for_stack_trace(body) "ExpressTemplates::Components::TableFor.render_in(self) {\n#{body}\n}" end def compile wrap_for_stack_trace(lookup(:markup)) end class Column attr :name, :options def initialize(name, options = {}) @name = name @options = options @formatter = options[:formatter] end def format(item_name) if @formatter.nil? "\#\{#{item_name}.#{name}\}" elsif @formatter.kind_of?(Proc) "\#\{(#{@formatter.source}).call(#{item_name}.#{name})\}" end end def title @name.to_s.try(:titleize) end end end end end