Filters
Defines the accessible attribute that is used to filter scope by the specified value with specified code.
Example:
class UserGrid
include Datagrid
scope do
User.order("users.created_at desc")
end
filter(:name)
filter(:posts_count, :integer) do |value|
self.where(["posts_count >= ?", value])
end
end
WARNING: Though it is possible to include order
in the default scope in a UserGrid class statement (as in the example above), it is usually a bad idea, because it will mess up or practically invalidate all the other user-specified column-based sorting; in the above example, sorting based on created_at
has always a higher priority than any other user-specified ordering. A much better method to specify the default ordering is specifying an option(s) of :order
and optionally :descending
in creating a Datagrid instance, usually in your Controller. The scope
here should be used exclusively for filtering the contents used for the table, not for sorting/ordering; if no filters are required, you can just write User.all
. See Ordering in Columns wiki for detail on how to specify the default ordering (it is a slightly obscured place...).
Each filter becomes grid attribute. In order to create grid that display all users with name 'John' that have more than zero posts:
grid = UserGrid.new(posts_count: 1, name: "John")
grid.assets # SELECT * FROM users WHERE users.posts_count > 1 AND name = 'John'
Important! Take care about non-breaking the filter chain and force objects loading in filter. The filter block should always return corresponding chainable ORM object (e.g.ActiveRecord::Relation) rather than Array
Filter block should have at least one argument that represents a value assigned to grid object attribute, as an example above. If no block given filter is generated automatically as simple select by filter name from scope.
Optionally you can pass a scope as second argument and datagrid object as third argument. (from Datagrid >= 0.6.1)
# this is the default
filter(:name, :string) # { |value| where(name: value) }
# alternative default when filter has same name as search scope
filter(:limit, :integer) # { |value| limit(value) }
# easy use case:
filter(:name, :string) { |value| where("name ilike ?", "%#{value}%") }
# use case without `instance_eval`
filter(:name, :string) { |value, scope| scope.where("name ilike ?", "%#{value}%") }
# very advanced use case
filter(:predicate, :enum, select: ["like", "ilike"], dummy: true)
filter(:name, :string) do |value, scope, grid|
scope.where("name #{grid.predicate} ?", "%#{value}%")
end
Filter does type conversion automatically. The following filter types are supported:
- default
- date
- datetime
- enum
- boolean
- xboolean
- integer
- float
- string
- dynamic
:default (default) - leave value as is
:date - converts value to date using date parser.
Has option :range - allows filter to accept a date range. You are able to customize a date format. See Configuration for more info.
# Date range filter defaults to last month
# Note that default date range is wrapped with proc to let datagrid recalculate it
# every time filter is rendered.
filter(:created_at, :date, range: true, default: proc { [1.month.ago.to_date, Date.today]})
:datetime - converts value to timestamp using time parser.
Has option :range - allows filter to accept a time range. You are able to customize a datetime format. See Configuration for more info
# Time range filter defaults to last 24 hours
# Note that default time range is wrapped with proc to let datagrid recalculate it
# every time filter is rendered.
filter(:created_at, :datetime, range: true, default: proc { [1.hour.ago, Time.now]})
:enum - designed to be collection select. Has additional options for easy form generation:
-
:select - required collection of values to match against
-
:include_blank
- set to true or a prompt string if the first option element of the select element is a blank. Default:true
. Ex:"--- Select ---"
-
:prompt
- set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt –"Please select"
– or the given prompt string. -
:checkboxes - when
true
datagrid_filter
will render checkboxes for each option instead of multi-select. This options implies:multiple
option automatically set to true.
# Static select option
filter(:user_type, :enum, select: ['Admin', 'Customer', 'Manager'])
# Dynamic select option
filter(:category_id, :enum, select: proc { Category.all.map {|c| [c.name, c.id] }}, multiple: true)
# Advanced cases
filter(:genre, :enum, select: ['Sci-fi', 'Fantasy'])
filter(:author, :enum, select: :available_authors)
def available_authors
Author.writes_in_genre(genre).map {|a| [a.name, a.id]}
end
IMPORTANT: Always wrap dynamic :select
option with proc, so that datagrid fetch it from database each time when it renders the form.
:boolean - converts value to true or false depending on whether it looks truly or not This filter is rarely used due to its restriction of something to be true or false while "i don't care" is not possible with this filter. Use :xboolean to have more control.
-
:integer - converts given value to integer.
- Has option :range - allows filter to accept an integer range.
# Integer range filter defaults to more than one post.
filter(:posts_count, :integer, range: true, default: [1, nil])
- :float - converts given value to float.
:xboolean - subtype of enum filter that provides select of "Yes", "No" and "Any". Localization example.
filter(:active, :xboolean)
- :string - converts given value to string
filter(:email, :string)
Provides a builder for dynamic SQL condition, that allows to specify a field, compare operation (equal, like, greater equal, less equal) and value you want to compare to
filter(:condition1, :dynamic)
filter(:condition2, :dynamic)
UsersGrid.new(condition1: [:name, "=~", "John"], condition2: [:posts_count, ">=", 1])
UsersGrid.assets # SELECT * FROM users WHERE name like '%John%' and posts_count >= 1
Operations display signs can be changed using Localization file.
Options:
-
:operations
- list of operations that should be available. With this option you can add your own operations and write an implementation on how to handle them in filter block argument. Default:['=', '=~', '>=', '<=']
. -
:select
- a list of fields that is available for selection. Fields can be given as array of column-label pair. Example:[['Id', :id], ['Name', :name], ['Content', :body]]
Options that could be passed to any filter type:
- :header - human readable name of the filter. Default: generated from the filter name.
filter(:id, :integer, header: "Identifier")
- :default - default value of the filter. Default: nil.
filter(:created_at, :date, range: true, default: proc { [1.month.ago.to_date, Date.today] })
-
:multiple - if true multiple values can be assigned to this filter. By default multiple values are parsed from string using
,
separator. But you can specify a different separator as option value. Default: false.
filter(:id, :integer, multiple: true)
Grid.new(:id => "1,2").assets # => select * from <table> where id in (1,2)
- :allow_nil - determines if filter should be called if filter value is nil. Default: false.
- :allow_blank - determines if filter should be called if filter value is #blank?. Default: false.
Filter label in the form can be specified with :header option.
filter(:created_at, :date, header: "Creation date")
By default it is generated from filter name. Also you can use localization file if you have multilanguage application.
Example: In order to localize filter :group_name in SimpleReport use the key datagrid.simple_report.filters.group_name
The other way is by use Proc or any callable object as value to :header
parameter.
Example:
filter(:created_at, :date, header: proc { I18n.t("creation_date") } )