Skip to content
Bogdan Gusiev edited this page Jan 7, 2024 · 51 revisions

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

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 types

Filter does type conversion automatically. The following filter types are supported:

  • default
  • date
  • datetime
  • enum
  • boolean
  • xboolean
  • integer
  • float
  • string
  • dynamic

Default

:default (default) - leave value as is

Date

: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

: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]})

Enumerable

: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

: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

  • :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

  • :float - converts given value to float.

Xboolean

:xboolean - subtype of enum filter that provides select of "Yes", "No" and "Any". Localization example.

filter(:active, :xboolean)

String

  • :string - converts given value to string
filter(:email, :string)

Dynamic

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]]

Filter options

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.

Localization

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") } )