-
Notifications
You must be signed in to change notification settings - Fork 116
Columns
Defines column that will be used to display data.
Example:
class UserGrid
include Datagrid
scope do
User.order("users.created_at desc").includes(:group)
end
column(:name)
column(:group, :order => "groups.name") do
self.group.name
end
column(:active, :header => "Activated") do |user|
!user.disabled
end
end
Each column will be used to generate data. In order to create grid that display all users:
grid = UserGrid.new
grid.header # => ["Group", "Name", "Disabled"]
grid.rows # => [
# ["Steve", "Spammers", true],
# [ "John", "Spoilers", true],
# ["Berry", "Good people", false]
# ]
grid.data # => Header & Rows
Column value can be defined by passing a block to Datagrid.column method.
If no block given column it is generated automatically by sending column name method to model.
column(:name) # => asset.name
The block could have no arguments(instance_eval for each asset will be used).
column(:completed) { completed? }
If you don't like instance_eval you can use asset as first argument:
column(:completed) { |asset| asset.completed? }
For the most complicated columns you can also pass datagrid object itself:
filter(:category) do |value|
where("category LIKE '%#{value}%'")
end
column(:exactly_matches_category) do |asset, grid|
asset.category == grid.category
end
Another advanced use case (version >= 0.9.3):
class MerchantsGrid
scope { Merchant }
# Dummy filter is not automatically applied to scope
filter(:period, :date, :range => true, :dummy => true)
column(:number_of_purchases) do |merchant, grid|
merchant.purchases.where(:created_at => grid.period).count
end
end
Sometimes you can even combine previously defined columns into new ones:
column(:total_sales) do |merchant|
merchant.purchases.sum(:subtotal)
end
column(:number_of_sales) do |merchant|
merchant.purchases.count
end
column(:average_order_value) do |_, _, row|
row.total_sales / row.number_of_sales
end
This allows to determine how a column should be selected. Right now it only supports ActiveRecord. If specified, it will add the string to the select. This is useful for data aggregation or to make transformations of the data directly in the database.
column(:count_of_users, 'count(user_id)')
column(:uppercase_name, 'upper(name)')
Note, that you should never specify the AS part, since it's added automatically.
Sometimes you might need a different formatting for column value in CSV and HTML. In this case you can use the following construction:
column(:name) do |asset|
format(asset.name) do |value|
content_tag(:strong, value)
end
end
Now when you render an HTML table you will see <strong>NAME</strong>
While in CSV (or any other non-HTML representation) it won't be wrapped with <strong>
tag.
You can specify if given column should only appear in html view (via the :html option) :
column(:completed, :html => true) do |asset|
asset.completed? ? image_tag("green.gif") : image_tag("red.gif")
end
# or do it in partial
column(:actions, :html => true) do |asset|
render :partial => "admin/assets/actions", :object => asset
end
# if you want to hide a column only from html view and have it only in csv export.
column(:id, :html => false)
You can enable grid level cache for column values:
self.cached = true
In this way column values will be cached based on models primary keys You can define other cache key when model don't have primary key:
self.cached = proc {|model| model.identifier }
It is also helpful when aggregation queries are made.
Each column supports the following options that is used to specify SQL to sort data by the given column:
-
:order - an order SQL that should be used to sort by this column.
Default: report column name if there is database column with this name. Passing
false
will disable the order for this column. - :order_desc - descending order expression from this column. Default: "#{order} desc".
# Basic use case
column(:group_name, :order => "groups.name") { self.group.name }
# Advanced use case
column(
:priority,
# suppose that models with null priority will be always on bottom
:order => "priority is not null desc, priority",
:order_desc => "prioritty is not null desc, priority desc"
)
# Disable order
column(:title, :order => false)
# Order by joined table
# Allows to join specified table only when order is enabled
# for performance
column(:profile_updated_at, :order => proc { |scope|
scope.joins(:profile).order("profiles.updated_at")
}) do |model|
model.profile.updated_at.to_date
end
In order to specify order the following attributes are used for Datagrid instance:
- :order - column name to sort with as Symbol. Default: nil.
- :descending - if true descending suffix is added to specified order. Default: false.
UserGrid.new(:order => :group, :descending => true).assets # => assets ordered by :group column descending
You can specify default options for entire grid by using default_column_options
accessor methods.
They still can be overwritten at column level.
# Disable default order
self.default_column_options = { :order => false }
# Makes entire report HTML
self.default_column_options = { :html => true }
You are able to show only specific columns in certain context.
The column_names
instance accessor can be used for that:
grid = UsersGrid.new
grid.data # => [["Id", "Name" "Disabled"], [1, "Allan", true], [2, "Bogdan", false]]
grid.column_names = [:id, :name]
grid.data # => [["Id", "Name], [1, "Allan"], [2, "Bogdan"]]
grid.column_names = nil # Reset to default
grid.data # => [["Id", "Name" "Disabled"], [1, "Allan", true], [2, "Bogdan", false]]
There is several column options that helps to control column names filter content
-
:mandatory
- makes column impossible to disable. Hides it fromcolumn_names_filter
selection
When you specify at least one mandatory
column in a grid the column visibility mechanism become different:
- only mandatory columns are displayed by default
- non-mandatory columns need to be explicitly enabled by
column_names
attribute
Example:
class Grid
include Datagrid
scope { User }
column(:id, :mandatory => true)
column(:name, :mandatory => true)
column(:category)
[:posts, :comments].each do |association|
column(:"number_of_#{association}") do |model|
model.send(association).count
end
end
end
grid = Grid.new
grid.data # => [["Id", "Name"], [1, "Bogdan Gusiev"], [2, "Dominic Coryell"]]
grid.column_names = ["category", "number_of_posts"]
grid.data # => [ ["Id", "Name", "Category", "Number of posts],
# [1, "Bogdan Gusiev", "developer", 5],
# [2, "Dominic Coryell", "manager", 3] ]
You can specify :if
and :unless
options to a column, to determine if the column should be shown or not. If a symbol is given, it will call that method on the grid, if a proc is given, it will be called with the grid as an argument.
Example:
column(:name, :if => :show_name?)
#equivalent:
column(:name, :unless => proc {|grid| !grid.show_name? })
# More realistic
filter(:category) do |value|
where("category like '%#{value}%'")
end
column(:exactly_match_category, :if => proc {|grid| grid.category.present?}) do |model, grid|
model.category == grid.category
end
In some cases you can not define columns at class level. So you can define columns on instance level
grid = MyGrid.new
grid.column(:extra_data) do |model|
model.extra_data
end
In this example extra_data
column will not be defined for any other MyGrid
instance in a project. Only for current instance stored in grid
local variable.
Same behaviour can be achieved by using dynamic
inside of grid class. More live example:
class CampaignsGrid
scope { Campaign }
filter(:account_id, :integer)
def account
Account.find(account_id)
end
dynamic do
account.sales_categories.each do |category|
column(:"sales_in_#{category.name}") do |campaign|
campaign.sales.where(category_id: category.id).sum(:subtotal)
end
end
end
end
Column selection can be available as select[multiple]
or several input[type=checkbox]
in datagrid form.
Use column_names_filter
to reach that behavior.
column_names_filter
accepts same options as :enum
filter.
column_names_filter(:header => "Column", :checkboxes => true)
In this case column names select
will only contain counter columns. id name category
columns will be always present.
You can manually specify which columns should be selectable by end user:
column_names_filter(:select => [:metric_one, :metric_two, :metric_three])
In this way you can hide columns from end user.
Column header can be specified with :header option:
column(:active, :header => "Activated")
By default it is generated from column name. Also you can use localization file if you have multilanguage application.
Example: In order to localize column :name in SimpleReport use the key datagrid.simple_report.columns.name