Module: Datagrid::Helper

Defined in:
lib/datagrid/helper.rb

Overview

Datagrid Frontend Guide

Description

The easiest way to start with Datagrid frontend is by using the generator:

rails generate datagrid:scaffold users

This command builds the controller, view, route, and adds built-in CSS.

Datagrid includes helpers and a form builder for easy frontend generation. If you need a fully-featured custom GUI, create your templates manually with the help of the Columns API.

Controller and Routing

Grids usually implement the index action of a Rails REST resource. Here's an example:

resources :models, only: [:index]

Use the GET method in the form, and the controller becomes straightforward:

class ModelsController < ApplicationController
  def index
    @grid = ModelsGrid.new(params[:my_report]) do |scope|
      scope.page(params[:page]) # See pagination section
    end
  end
end

To apply additional scoping conditions, such as visibility based on the current user:

ModelsGrid.new(params[:my_report]) do |scope|
  scope.where(owner_id: current_user.id).page(params[:page])
end

To pass an object to a grid instance, define it as an accessible attribute:

class ModelsGrid
  attr_accessor :current_user
end

Then pass it when initializing the grid:

ModelsGrid.new(params[:models_grid].merge(current_user: current_user))

Form Builder

Basic Method

Use the built-in partial:

= datagrid_form_with model: @grid, url: report_path, other_form_for_option: value

#datagrid_form_with supports the same options as Rails form_with.

Advanced Method

You can use Rails built-in tools to create a form. Additionally, Datagrid provides helpers to generate input/select elements for filters:

- form_with model: UserGrid.new, method: :get, url: users_path do |f|
  %div
    = f.datagrid_label :name
    = f.datagrid_filter :name # => <input name="grid[name]" type="text"/>
  %div
    = f.datagrid_label :category_id
    = f.datagrid_filter :category_id # => <select name="grid[category_id]">....</select>

For more flexibility, use Rails default helpers:

%div
  = f.label :name
  = f.text_field :name

See the localization section of Filters.

Datagrid Table

Use the helper to display a report:

%div== Total #{@grid.assets.total}
= datagrid_table(@report)
= will_paginate @report.assets

Options:

  • :html - Attributes for the <table> tag.
  • :order - Set to false to disable ordering controls (default: true).
  • :columns - Specify an array of column names to display.

Pagination

Datagrid is abstracted from pagination but integrates seamlessly with tools like Kaminari, WillPaginate, or Pagy:

# Kaminari
@grid = MyGrid.new(params[:grid]) do |scope|
  scope.page(params[:page]).per(10)
end

# WillPaginate
@grid = MyGrid.new(params[:grid]) do |scope|
  scope.page(params[:page]).per_page(10)
end

# Pagy
@grid = MyGrid.new(params[:grid])
@pagy, @records = pagy(@grid.assets)

Render the paginated collection:

# WillPaginate or Kaminari
<%= datagrid_table(@grid, options) %>
# Pagy
<%= datagrid_table(@grid, @records, options) %>

CSV Export

Add CSV support to your controller:

class UsersController < ApplicationController
  def index
    @grid = UsersGrid.new(params[:users_grid])
    respond_to do |f|
      f.html { @grid.scope { |scope| scope.page(params[:page]) } }
      f.csv do
        send_data @grid.to_csv, type: "text/csv", disposition: 'inline', filename: "grid-#{Time.now.to_s}.csv"
      end
    end
  end
end

Add a button in your interface:

link_to "Get CSV", url_for(format: 'csv', users_grid: params[:users_grid])

AJAX

Datagrid supports asynchronous data loading. Add this to your controller:

if request.xhr?
  render json: {table: view_context.datagrid_table(@grid)}
end

Modify the form for AJAX:

= datagrid_form_with model: @grid, html: {class: 'js-datagrid-form'}
.js-datagrid-table
  = datagrid_table @grid
.js-pagination
  = paginate @grid.assets
:javascript
  $('.js-datagrid-form').submit(function(event) {
    event.preventDefault();
    $.get($(this).attr("action"), $(this).serialize(), function (data) {
      $('.js-datagrid-table').html(data.table);
    });
  });

Modifying Built-In Partials

To customize Datagrid views:

rake datagrid:copy_partials

This creates files in app/views/datagrid/, which you can modify to suit your needs:

app/views/datagrid/
├── _enum_checkboxes.html.erb # datagrid_filter for filter(name, :enum, checkboxes: true)
├── _form.html.erb            # datagrid_form_with
├── _head.html.erb            # datagrid_header
├── _range_filter.html.erb    # datagrid_filter for filter(name, type, range: true)
├── _row.html.erb             # datagrid_rows/datagrid_rows
└── _table.html.erb           # datagrid_table

Custom Options

You can add custom options to Datagrid columns and filters and implement their support on the frontend. For example, you might want to add a description option to a column that appears as a tooltip on mouseover.

column(
  :aov, header: 'AOV',
  description: 'Average order value: sum of orders subtotal divided by their count'
) do |category|
  category.orders.sum(:subtotal) / category.orders.count
end

The :description option is not built into Datagrid, but you can implement it by adding the following to partial app/views/datagrid/_header.html.erb:

<% if column.options[:description] %>
  <a data-toggle="tooltip" title="<%= column.options[:description] %>">
    <i class="icon-question-sign"></i>
  </a>
<% end %>

This modification allows the :description tooltip to work with your chosen UI and JavaScript library. The same technique can be applied to filters by calling filter.options in corresponding partials.

Highlight Rows

To add custom HTML classes to each row for styling, modify the _row.html.erb partial:

-<tr>
+<tr class="<%= grid.respond_to?(:row_class) ? grid.row_class(asset) : "" %>">
   <% grid.html_columns(*options[:columns]).each do |column| %>
     <td class="<%= datagrid_column_classes(grid, column) %>">
       <%= datagrid_value(grid, column, asset) %>
     </td>
   <% end %>

This allows you to define a custom row_class method in your grid class, like this:

class IssuesGrid < ApplicationGrid
  scope { Issue }

  def row_class(issue)
    case issue.status
    when "fixed" then "green"
    when "rejected" then "red"
    else "blue"
    end
  end
end

Localization

You can overwrite Datagrid’s custom localization keys at the application level. See the localization keys here:

https://github.com/bogdan/datagrid/blob/master/lib/datagrid/locale/en.yml

Defined Under Namespace

Classes: HtmlRow

Instance Method Summary collapse

Instance Method Details

#datagrid_form_for(grid, options = {}) ⇒ String

Deprecated.

Use #datagrid_form_with instead.

Renders HTML for grid with all filters inputs and labels defined in it

Supported options:

  • :partials - Path for form partial lookup. Default: 'datagrid'.
  • All options supported by Rails form_with helper

Parameters:

  • grid (Datagrid::Base)

    grid object

  • options (Hash) (defaults to: {})

Returns:

  • (String)

    form HTML tag markup



389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/datagrid/helper.rb', line 389

def datagrid_form_for(grid, options = {})
  Datagrid::Utils.warn_once("datagrid_form_for is deprecated if favor of datagrid_form_with.")
  _render_partial(
    "form", options[:partials],
    grid: grid,
    options: {
      method: :get,
      as: grid.param_name,
      local: true,
      **options,
    },
  )
end

#datagrid_form_with(**options) ⇒ String

Renders HTML for grid with all filters inputs and labels defined in it

Parameters:

  • options (Hash{Symbol => Object})

Options Hash (**options):

  • partials (String)

    Path for form partial lookup. Default: 'datagrid', which uses app/views/datagrid/ partials. Example: 'datagrid_admin' uses app/views/datagrid_admin partials.

  • model (Datagrid::Base)

    a Datagrid object to be rendered.

  • All (Hash)

    options supported by Rails form_with helper.

Returns:

  • (String)

    form HTML tag markup

Raises:



369
370
371
372
373
374
375
376
# File 'lib/datagrid/helper.rb', line 369

def datagrid_form_with(**options)
  raise ArgumentError, "datagrid_form_with block argument is invalid. Use form_with instead." if block_given?

  grid = options[:model]
  raise ArgumentError, "Grid has no available filters" if grid&.filters&.empty?

  _render_partial("form", options[:partials], { grid: options[:model], options: options })
end

#datagrid_header(grid, opts = :__unspecified__, **options) ⇒ String

Renders HTML table header for given grid instance using columns defined in it

Parameters:

  • grid (Datagrid::Base)

    grid object

  • opts (Object) (defaults to: :__unspecified__)

    (deprecated) pass keyword arguments instead

  • options (Hash)

Options Hash (**options):

  • order (Boolean)

    Whether to display ordering controls built into the header. Default: true.

  • columns (Array<Symbol,String>)

    An array of column names to display. Use this when the same grid class is used in different contexts and requires different columns. Default: all defined columns.

  • partials (String)

    The path for partials lookup. Default: 'datagrid'.

Returns:

  • (String)

    HTML table header tag markup



307
308
309
310
311
312
313
314
315
316
# File 'lib/datagrid/helper.rb', line 307

def datagrid_header(grid, opts = :__unspecified__, **options)
  unless opts == :__unspecified__
    Datagrid::Utils.warn_once("datagrid_header now requires ** operator when passing options.")
    options.reverse_merge!(opts)
  end
  options[:order] = true unless options.key?(:order)

  _render_partial("head", options[:partials],
    { grid: grid, options: options },)
end

#datagrid_order_for(grid, column, options = {}) ⇒ String

Deprecated.

Put necessary code inline inside datagrid/head partial. See built-in partial for example.

Returns renders ordering controls for the given column name.

Parameters:

Options Hash (options):

  • partials (String)

    The path for partials lookup. Default: 'datagrid'.

Returns:

  • (String)

    renders ordering controls for the given column name



351
352
353
354
355
356
357
358
359
# File 'lib/datagrid/helper.rb', line 351

def datagrid_order_for(grid, column, options = {})
  Datagrid::Utils.warn_once(<<~MSG)
    datagrid_order_for is deprecated.
    Put necessary code inline inside datagrid/head partial.
    See built-in partial for example.
  MSG
  _render_partial("order_for", options[:partials],
    { grid: grid, column: column },)
end

#datagrid_order_path(grid, column, descending) ⇒ String

Generates an ascending or descending order url for the given column

Parameters:

Returns:

  • (String)

    order layout HTML markup



434
435
436
437
438
439
440
441
# File 'lib/datagrid/helper.rb', line 434

def datagrid_order_path(grid, column, descending)
  column = grid.column_by_name(column)
  query = request&.query_parameters || {}
  ActionDispatch::Http::URL.path_for(
    path: request&.path || "/",
    params: query.merge(grid.query_params(order: column.name, descending: descending)),
  )
end

#datagrid_row(grid, asset, **options, &block) ⇒ Datagrid::Helper::HtmlRow, String

Provides access to datagrid columns data. Used in case you want to build html table completelly manually

Examples:

Render default layout for row

<%= datagrid_row(grid, user, columns: [:first_name, :last_name, :actions]) %>

Rendering custom layout for first_name and last_name columns

<%= datagrid_row(grid, user) do |row| %>
  <tr>
    <td><%= row.first_name %></td>
    <td><%= row.last_name %></td>
  </tr>
<% end %>

Rendering custom layout passing a block

<% row = datagrid_row(grid, user) %>
First Name: <%= row.first_name %>
Last Name: <%= row.last_name %>

Parameters:

  • grid (Datagrid::Base)

    grid object

  • asset (Object)

    object from grid scope

  • block (Proc)

    block with Datagrid::Helper::HtmlRow as an argument returning a HTML markup as a String

  • options (Hash{Symbol => Object})

Returns:



423
424
425
426
427
# File 'lib/datagrid/helper.rb', line 423

def datagrid_row(grid, asset, **options, &block)
  Datagrid::Helper::HtmlRow.new(self, grid, asset, options).tap do |row|
    return capture(row, &block) if block_given?
  end
end

#datagrid_rows(grid, assets = grid.assets, **options, &block) ⇒ String

Renders HTML table rows using given grid definition using columns defined in it. Allows to provide a custom layout for each for in place with a block

Examples:

Generic table rows Layout

= datagrid_rows(grid)

Custom Layout

= datagrid_rows(grid) do |row|
  %tr
    %td= row.project_name
    %td.project-status{class: row.status}= row.status

Parameters:

  • grid (Datagrid::Base)

    datagrid object

  • assets (Array<Object>) (defaults to: grid.assets)

    assets as per defined in grid scope

  • options (Hash)

    a customizable set of options

Options Hash (**options):

  • columns (Array<Symbol>)

    An array of column names to display. Use this when the same grid class is used in different contexts and requires different columns. Default: all defined columns.

  • partials (String)

    The path for partials lookup. Default: 'datagrid'.

Returns:

  • (String)


336
337
338
339
340
341
342
# File 'lib/datagrid/helper.rb', line 336

def datagrid_rows(grid, assets = grid.assets, **options, &block)
  safe_join(
    assets.map do |asset|
      datagrid_row(grid, asset, **options, &block)
    end.to_a,
  )
end

#datagrid_table(grid, assets = grid.assets, **options) ⇒ String

Renders html table with columns defined in grid class. In the most common used you need to pass paginated collection to datagrid table because datagrid do not have pagination compatibilities:

Examples:

assets = grid.assets.page(params[:page])
datagrid_table(grid, assets, options)

Parameters:

  • grid (Datagrid::Base)

    grid object

  • assets (Array) (defaults to: grid.assets)

    objects from grid scope

  • options (Hash{Symbol => Object})

    HTML attributes to be passed to <table> tag

Options Hash (**options):

  • html (Hash)

    A hash of attributes for the <table> tag.

  • order (Boolean)

    Whether to generate ordering controls. If set to false, ordering controls are not generated. Default: true.

  • columns (Array<Symbol>)

    An array of column names to display. Use this when the same grid class is used in different contexts and requires different columns. Default: all defined columns.

  • partials (String)

    The path for partials lookup. Default: 'datagrid'.

Returns:

  • (String)

    table tag HTML markup



283
284
285
286
287
288
289
290
291
292
# File 'lib/datagrid/helper.rb', line 283

def datagrid_table(grid, assets = grid.assets, **options)
  _render_partial(
    "table", options[:partials],
    {
      grid: grid,
      options: options,
      assets: assets,
    },
  )
end

#datagrid_value(grid, column, model) ⇒ Object

Returns individual cell value from the given grid, column name and model.

Examples:

<ul>
  <% @grid.columns.each do |column|
    <li><%= column.header %>: <%= datagrid_value(@grid, column.name, @resource %></li>
  <% end %>
</ul>

Parameters:

Returns:

  • (Object)

    individual cell value from the given grid, column name and model



255
256
257
258
259
# File 'lib/datagrid/helper.rb', line 255

def datagrid_value(grid, column, model)
  column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)

  grid.html_value(column, self, model)
end