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

Description

The easiest way to start with datagrid frontend is using the generator.

rails generate datagrid:scaffold <model in plural> # e.g. users

This will build controller, view, route and add built-in CSS.

Datagrid includes helpers and form builder for easy front end generation. If you need to build full featured custom GUI you should create your templates manually with the help of Columns API.

Controller and Routing

Grids in most cases implement index of a Rails REST resources. It is the only one action you need to display grid:

resources :models, only: [:index]

In this case GET method should be always used in a form. And controller will look damn simple:

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

Additional scoping conditions can be applied in case there is visibility limitation based on current user or any other context:

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, simply define it as accessible attribute:

class ModelsGrid
  ...
  attr_accessor :current_user
  ...
end

The attribute will be automatically assigned when you merge it to parameters in constructor:

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

Form builder

Basic method

Use datagrid built-in partial:

= datagrid_form_for @grid, url: report_path, other_form_for_option: value

datagrid_form_for supports the same options as Rails form_for helper.

Advanced method

In order to create a form you can use all set of rails built-in tools. In addition Datagrid provides you magic helper to generate input/select for corresponding filter type:

(haml for readability)

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

The easiest way to create a report form:

- form_for @report, method: :get, url: users_path do |f|
  - @report.filters.each do |filter|
    %div
      = f.datagrid_label filter
      = f.datagrid_filter filter
  = f.submit

If datagrid_filter doesn't provide enough flexibility, default rails helpers can do the job:

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

See also localization section of Filters

Datagrid table

There is a simple helper set of helpers that allows you display report. The most common way of doing it is (require any pagination gem, will_paginate gem is used as an example):

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

Supported options:

  • :html - hash of attributes for <table> tag
  • :order - If false do not generate ordering controlls. Default: true.
  • :columns - Array of column names to display. Used in case when same grid class is used in different places and needs different columns. Default: all defined columns.

This will create generic table from paginated assets with all defined columns and sorting controlls.

See also Localization section of Columns

Pagination

Datagrid is abstract from pagination.

It has only one pagination-sensitive helper: datagrid_table.

In order to add pagination, add the following in your controllers:

# 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)

And then render paginated collection:

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

Custom table layout

You are able to build table almost from scratch using the instance API of Datagrid object like:

grid.columns # => Array of columns
grid.row_for(model) # => Array of column values for given object
grid.scope # => Scope defined in grid
grid.hash_for(model) # => Hash with column names as keys and column values for given model as values
grid.header # => Array of header labels

Here is an example of custom layout:

%ol
  - @grid.assets.each.with_index do |asset, index|
    %li
      == Item #{index+1}:
      %ul
        - @grid.columns.each do |column|
          %li
            = column.header
            &mdash;
            = datagrid_value(@grid, column, asset)}

More info here: http://rubydoc.info/gems/datagrid/Datagrid/Columns/InstanceMethods

Additional Frontend helpers are also available:

datagrid_header(grid) # Renders HTML table header for given grid instance using columns defined in it.
datagrid_order_for(grid, column_name) # Renders ordering controls for the given column name.
datagrid_row(grid, asset) # Provides access to datagrid columns data.
datagrid_rows(grid, assets) # Renders HTML table rows using given grid definition using columns defined in it.
datagrid_value(grid, column, asset) # Returns an HTML value for given asset and column within the grid

More info here: http://rubydoc.info/gems/datagrid/Datagrid/Helper

CSV export

In order to support it extend your controller to handle csv response format:

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

Now place the button in the interface like:

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

AJAX

Datagrid provides a trivial way of asyncronous loading of data into datagrid table.

Put the following to your controller:

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

Modify the form to perform AJAX load submit and data load:

= datagrid_form_for @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

If You need serious customisation of datagrid helpers and all customisation options doesn't help, you can customise datagrid internal views by running:

rake datagrid:copy_partials

This will create the following files in your rails root directory:

app/views/datagrid/
├── _enum_checkboxes.html.erb
├── _form.html.erb
├── _head.html.erb
├── _order_for.html.erb
├── _range_filter.html.erb
├── _row.html.erb
└── _table.html.erb

Now You are able to customize whatever You want.

Custom Options

You are able to add a custom options to Datagrid columns and filters and implement their support on frontend. For example: Columns need to have description beside header that only appears on mouse over on column header.

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

:description is not a built in option of Datagrid, but it can be your own. In order to implement it modify the column header partial app/views/datagrid/_header.html.erb to use it:

 %tr
   - grid.html_columns(*options[:columns]).each do |column|
     %th{class: datagrid_column_classes(grid, column)}
       = column.header
+      - if column.options[:description]
+        %a{data: {toggle: 'tooltip', title: column.options[:description]}}
+          %i.icon-question-sign
       - if column.order && options[:order]
         = datagrid_order_for(grid, column, options)

In this description tooltip will work with UI you want and your favourite JavaScript library.

Same technique can be applied to filters by calling filter.options in corresponding partials.

Highlight rows

If you would like to attach a different html class to each row making it stylable with CSS, you add it this configuration option by modifying built-in partial _row.html.erb:

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

It will make it possible to define column HTML class in each grid like this:

class IssuesGrid
  include Datagrid
  scope { Issue }

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

Filter input HTML options

Modify _form.html.erb:

-      <%= f.datagrid_filter filter %>
+      <%= f.datagrid_filter filter, filter.options[:input_options] %>

Now you can specify :input_options for any filter definition:

filter(:username, :string, input_options: {
  maxlength: 10, placeholder: 'Specify Login Name'
})

Localization

Here are all datagrid custom localization keys that you can overwrite at application level:

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