Intro

WiceGrid is a Rails grid plugin.

One of the goals of this plugin was to allow the programmer to define the
contents of the cell by himself, just like one does when rendering a
collection via a simple table (and this is what differentiates WiceGrid
from various scaffolding solutions), but automate implementation of
filters, ordering, paginations, CSV export, and so on. Ruby blocks provide
an elegant means for this.

WiceGrid builds the call to the ActiveRecord layer for you and creates a
table view with the results of the call including:

paging

sortable columns

filtering by multiple columns

CSV export

saving queries

All working nicely together. Filters are added automatically according to
the type of the underlying DB column. Filtering by more than one column at
the same time is possible. More than one such grid can appear on a page,
and manipulations with one grid do not have any impact on the other.

WiceGrid does not use AJAX calls to reload itself, instead simple GET
requests are used for this, nevertheless, all other page parameters are
respected and preserved. WiceGrid views do not contain forms so you can
include it in your own forms.

How-To

Installation

If your application is Prototype-based , run the following generator to
copy all plugin assets:

rails g wice_grid:wice_grid_assets_prototype

For jQuery:

rails g wice_grid:wice_grid_assets_jquery

This will copy images, stylesheets, configuration file
config/initializers/wice_grid_config.rb, and the correct version
of javascript files.

Support for Prototype and jQuery

WiceGrid started as a plugin using the Prototype javascript framework.
Support for jQuery was added for version 0.6. The plugin contains two
versions of the main javascript file wice_grid.js, and depending
on the generator run to install the assets
(wice_grid_assets_prototype or wice_grid_assets_jquery)
the correct file is copied into public/javascripts.

Some javascript code is also generated and injected into the HTML page. The
value of constant Wice::Defaults::JS_FRAMEWORK in configuration
file wice_grid_config.rb defines for which JS framework code
should be generated. Generators wice_grid_assets_prototype and
wice_grid_assets_jquery create a version of
wice_grid_config.rb with the corresponding value of
Wice::Defaults::JS_FRAMEWORK.

In the Prototype mode the plugin uses a forked version of Calendarview. It is
bundled with the plugin and the generator task
wice_grid_assets_prototype will install al necessary assets.

The jQuery version uses jQuery datepicker. Because this
is part of the standard jQuery codebase, it is not bundled together with
the plugin, and it is the responsibility of the programmer to include all
necessary assets including localization files if the application is
multilingual.

It is always possible to fall back to simple dropdown lists that the
standard date/datetime Rails helpers use.

JQUERY DATEPICKER IS ONLY USED FOR DATE FILTERS AT THE MOMENT. DATETIME
FILTERS FALL BACK TO THE DEFAULT RAILS DATE/DATETIME HELPERS.

Basics

The Prototype version of WiceGrid requires prototype.js and
effects.js in order to function. The jQuery version of WiceGrid
requires jquery.js, the Datepicker widget from jQuery UI, and the
Highlight and Fade effects for the saved queries functionality.

Thus, make sure you require all necessary JS assets in your layout
template.

To include WiceGrid javascript and stylesheet files to the page use helper
include_wice_grid_assets :

<%= include_wice_grid_assets %>

The default behavior of include_wice_grid_assets is a bit magical
in the sense that it produces no output if the page contains no grids, so
don't panic if you see no WiceGrid includes. If you prefer to always
have WiceGrid stylesheet and javascript, use

<%= include_wice_grid_assets :load_on_demand => false %>

The simplest example of a WiceGrid for one simple DB table called
ApplicationAccount is the following:

Controller:

@tasks_grid = initialize_grid(Task)

It is also possible to use an ActiveRecord::Relation instance as the first
argument:

@tasks_grid = initialize_grid(Task.where(:active => true))

View:

<%= grid(@tasks_grid) do |g|
g.column do |task|
task.id
end
g.column do |task|
task.title
end
g.column do |task|
task.description
end
g.column do |task|
task.archived? ? 'Yes' : 'No'
end
g.column do |task|
link_to('Edit', edit_task_path(task))
end
end -%>

Code g.column do |task| ... end defines everything related to a
column in the resulting view table including column names, sorting,
filtering, the content of the column cells, etc. The return value of the
block is the table cell content.

In the above view code five columns were defined, all without names, no
sorting or filtering is available. Still, pagination becomes active if the
number of all extracted records exceeds the default number of rows per
page.

This will add sorting links and filters for columns Username and
Active. The plugin automatically creates filters according to the
type of the database column. In the above example a text field will be
created for column Title (title is a string), for column Archived
a dropdown filter will be created with options 'Yes', 'No',
and '–', and for the integer ID two short text fields are added
which can contain the numeric range (more than, less than).

It is important to remember that :attribute_name is the name of
the database column, not a model attribute. Of course, all database columns
have corresponding model attributes, but not all model attributes map to
columns in the same table with the same name.

Read more about available filters in the documentation for the column
method. Read the section about custom dropdown filters for more advanced
filters.

It is important to understand that it is up to the developer to make sure
that the value returned by a column block (the content of a cell)
corresponds to the underlying database column specified by
:attribute_name (and :model_class discussed below).

Rendering filter panel

The filter panel can be shown and hidden clicking the icon with binoculars.

The way the filter panel is shown after the page is loaded is controlled
via parameter :show_filters of the grid helper. Possible
values are:

:when_filtered - the filter is shown when the current table is the
result of filtering

:always - show the filter always

:no - never show the filter

Example:

<%= grid(@tasks_grid, :show_filters => :always) do |g|
......
end -%>

Filter related icons (filter icon, reset icon, show/hide icon) are placed
in the header of the last column if it doesn't have any filter or a
column name, otherwise an additional table column is added. To always place
the icons in the additional column, set
Wice::Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS to
false in the configuration file.

Initial Ordering

Initializing the grid we can also define the column by which the record
will be ordered on the first rendering of the grid, when the user
has not set their ordering setting by clicking the column label, and the
order direction:

Please note that though all queries inside of WiceGrid are run without the
default scope, if you use an ActiveRecord::Relation instance to initialize
grid, it will already include the default scope. Thus you might consider
using unscoped:

Please note that the blockless definition of the column only works with
columns from the main table and it won't work with columns with
:model_class

Joined associations referring to the same table

In case there are two joined associations both referring to the same table,
ActiveRecord constructs a query where the second join provides an alias for
the joined table. To enable WiceGrid to order and filter by columns
belonging to different associations but originating from the same table,
set :table_alias to this alias:

Custom Ordering

It is possible to change the way results are ordered injecting a piece of
SQL code, for example, use ORDER BY INET_ATON(ip_address) instead
of ORDER BY ip_address.

To do so, provide parameter :custom_order in the initialization of
the grid with a hash where keys are fully qualified names of database
columns, and values the required chunks of SQL to use in the ORDER BY
clause.

Custom dropdown filters

It is possible to construct custom dropdown filters. Depending on the value
of column parameter:custom_filter different modes are
available:

Array of two-element arrays or a hash

An array of two-element arrays or a hash are semantically identical ways of
creating a custom filter.

Every first item of the two-element array is used for the label of the
select option while the second element is the value of the select option.
In case of a hash the keys become the labels of the generated dropdown
list, while the values will be values of options of the dropdown list:

Note that in the above example all names of all possible categories will
appear even if they don't appear in the current resultset. To only show
those which do appear in the resutset, use an array of symbol messages (see
section 'An array of symbols').

Custom filters and associations (joined tables)

In most cases custom fields are required for one-to-many and many-to-many
associations.

To correctly build a filter condition foreign keys have to be matched.

is bad style and can fail, because the resulting condition will compare the
name of the project, projects.name to a string, and in some
databases it is possible that different records (projects in our example)
have the same name.

To use filter with foreign keys, we have to change the declaration of the
column from projects.name, to tasks.project_id, and build
the dropdown with foreign keys as values:

Any other symbol (method name) or an array of symbols (method names)

For one symbol (different from :auto) the dropdown list is
populated by all unique values returned by the method with this name sent
to all ActiveRecord objects throughout all pages.

The conditions set up by the user are ignored, that is, the records used
are all those found on all pages without any filters active.

For an array of symbols, the first method name is sent to the ActiveRecord
object if it responds to this method, the second method name is sent to the
returned value unless it is nil, and so on. In other words, a
single symbol mode is the same as an array of symbols where the array
contains just one element.

The method does not have to be a field in the result set, it is just some
value computed in the method after the database call and ActiveRecord
instantiation.

Filtering by any option of such a custom filter will bring a non-empty
list, unlike with :auto.

This mode has one major drawback - this mode requires an additional query
without offset and limit clauses to instantiate
all ActiveRecord objects, and performance-wise it brings all the
advantages of pagination to nothing. Thus, memory- and performance-wise
this can be really bad for some queries and tables and should be used with
care.

If the final method returns a atomic value like a string or an integer, it
is used for both the value and the label of the select option element:

<option value="returned value">returned value</option>

However, if the retuned value is a two element array, the first element is
used for the option label and the second - for the value.

Typically, a model method like the following:

def to_option
[name, id]
end

together with

:custom_filter => :to_option

would do the trick:

<option value="id">name</option>

Alternatively, a hash with the single key-value pair can be used, where the
key will be used for the label, and the key - for the value:

def to_option
{name => id}
end

Special treatment of values 'null' and 'not null'

Values 'null' and 'not null' in a generated custom filter
are treated specially, as SQL null statement and not as strings.
Value 'null' is transformed into SQL condition IS NULL,
and 'not null' into IS NOT NULL

Defaults

Default values like can be changed in
config/initializers/wice_grid_config.rb, as well grid labels and
paths to some images.

Submit/Reset buttons

View helper submit_grid_javascript returns javascript which
applies current filters. View helper reset_grid_javascript returns
javascript which resets the grid, clearing the state of filters. This
allows to create your own Submit and Reset buttons anywhere on the page
with the help of button_to_function:

To complement this feature there are two parameters in the grid
helper :hide_submit_button and :hide_reset_button which
hide default buttons in the grid if set to true.

Auto-reloading filters

It is possible to configure a grid to reload itself once a filter has been
changed. It works with all filter types including the JS calendar, the only
exception is the standard Rails date/datetime filters.

Naturally, there can be only one row_attributes definition for a
WiceGrid instance.

Various classes do not overwrite each other, instead, they are
concatenated.

WiceGrid icons are in directory public/images/icons/grid/.

Adding rows to the grid

It is possible to add your own handcrafted HTML after and/or before each
grid row. This works similar to row_attributes, by adding blocks
after_row, before_row, and last_row:

<%= grid(@tasks_grid) do |g|
g.before_row do |task|
if task.active?
"<tr><td colspan=\"10\">Custom line for #{t.name}</td></tr>" # this would add a row
# before every active task row
else
nil
end
end
g.last_row do # This row will always be added to the bottom of the grid
content_tag(:tr,
content_tag(:td,
'Last row',
:colspan => 10),
:class => 'last_row')
end
.......
end %>

It is up for the developer to return the correct HTML code, or return
nil if no row is needed for this record. Naturally, there is only
one before_row definition and one after_row definition
for a WiceGrid instance.

A real life example might be some enterprisy tables inside a table.

Rendering a grid without records

If the grid does not contain any records to show, it is possible show some
alternative view instead of an empty grid. Bear in mind that if the user
sets up the filters in such a way that the selection of records is empty,
this will still render the grid and it will be possible to reset the grid
clicking on the Reset button. Thus, this only works if the initial number
of records is 0.

<%= grid(@grid) do |g|
g.blank_slate do
"There are no records"
end
g.column do |product|
...
end
end -%>

There are two alternative ways to do the same, submitting a string to
blank_slate:

g.blank_slate "some text to be rendered"

Or a partial:

g.blank_slate :partial => "partial_name"

Localization

Versions of WiceGrid newer than version 0.5 support localization via Rails
I18n, however, without breaking compatibility with pre-I18n versions of
Rails (2.1.0 and older).

Running

./script/generate wice_grid_assets

will copy file wice_grid.yml to config/locales.

The current locale is taken from I18n.locale. The locale also
propagates to the javascript calendar widget (see
Calendar.messagebundle in calendarview.js to add
languages).

If the messages are not found, or I18n is missing, WiceGrid will fall back
to the hardcoded messages in
config/initializers/wice_grid_config.rb.

Currently supported languages are English, Dutch, French, and Russian.

ERB mode

The view helper can function in two different modes. These are defined by
its erb_mode parameter. By default (:erb_mode =>
false) the view helper is a simple helper surrounded by
<%= and %>, like in all examples above.

The second mode (:erb_mode => true) is called ERB mode
and it allows to embed any ERB content inside blocks, which is basically
the style of the form_for helper, only form_for takes one
block, while inside the grid block there are other method calls
taking blocks as parameters:

This mode can be usable if you like to have much HTML code inside cells.

Please remember that in this mode the helper opens with <%
instead of <%=, similar to form_for.

The default value for :show_filters can be changed in
config/initializers/wice_grid_config.rb.

Action Column

It is easy to add a column with checkboxes for each record. This is useful
for actions with multiple records, for example, deleting selected records.
Please note that action_column only creates the checkboxes and the
'Select All' and 'Deselect All' buttons, and the form
itself as well as processing the parameters should be taken care of by the
application code.

By default the name of the HTTP parameter follows pattern
"#{grid_name}[#{param_name}][]", thus
params[grid_name][param_name] will contain an array of object IDs.

See the documentation for more details.

Integration of the grid with other forms on page

Imagine that the user should be able to change the behavior of the grid
using some other control on the page, and not a grid filter.

For example, on a page showing tasks, change between 'Show active
tasks' to 'Show archived tasks' using a dropdown box. WiceGrid
allows to keep the status of the grid with all the filtering and sorting
using helper dump_filter_parameters_as_hidden_fields which takes a
grid object and dumps all current sorting and filtering parameters as
hidden fields. Just include
dump_filter_parameters_as_hidden_fields(@grid) inside your form,
and the newly rendered grid will keep ordering and filtering.

Javascript Calendar for Date and DateTime Filters.

Standard Rails Date and DateTime helpers are a set of dropdown lists, and
while this is practical, displaying two Date or especially DateTime helpers
takes too much space on a page and is in general confusing.

To solve this, WiceGrid includes a second variant of Date/DateTime filters
based on a Javascript calendar.

In the Prototype mode the plugin uses a forked version of Calendarview. It is
bundled with the plugin and the generator task
wice_grid_assets_prototype will install al necessary assets.

The jQuery version uses jQuery datepicker. Because this
is part of the standard jQuery codebase, it is not bundled together with
the plugin, and it is the responsibility of the programmer to include all
necessary assets including localization files if the application is
multilingual.

It is always possible to fall back to simple dropdown lists that the
standard date/datetime Rails helpers use.

JQUERY DATEPICKER IS ONLY USED FOR DATE FILTERS AT THE MOMENT. DATETIME
FILTERS FALL BACK TO THE DEFAULT RAILS DATE/DATETIME HELPERS.

Calendar based helpers are enabled by default, but it's possible to
change it in config/initializers/wice_grid_config.rb, variable
HELPER_STYLE.

The flavor of the date filter can also be changed on per-column basis:

Constants DATE_FORMAT and DATETIME_FORMAT define the
format of dates the user will see, as well as the format of the string sent
in a HTTP parameter.

You can change the constants in
config/initializers/wice_grid_config.rb. Doing so, make sure that
lamdbas defined in DATETIME_PARSER and DATE_PARSER return
valid DateTime and Date objects. The format by default is %Y-%m-%d
for the Date and the date part of DateTime, and Time.zone.parse
and Date.parse handle it. Make sure it stays so.

jQuery datepicker uses a different format flavor, therefore there
is an additional constant DATE_FORMAT_JQUERY. While
DATE_FORMAT_JQUERY is fed to datepicker,
DATE_FORMAT is still used for presenting initial date values in
filters, so make sure that DATE_FORMAT_JQUERY and
DATE_FORMAT result in an identical date representation.

Show All Records

It is possible to switch to the All Records mode clicking on link “show
all” in the bottom right corner. This functionality should be used with
care. To turn this mode off for all grid instances, change constant
ALLOW_SHOWING_ALL_QUERIES in
config/initializers/wice_grid_config.rb to false. To do
so for a specific grid, use initializer parameter
:allow_showing_all_records.

Configuration constant START_SHOWING_WARNING_FROM sets the
threshold number of all records after which clicking on the link results in
a javascript confirmation dialog.

CSV Export

It is possible to export the data displayed on a grid to a CSV file. The
dumped data is the current resultset with all the current filters and
sorting applied, only without the pagination constraint (i.e. all pages).

To enable CSV export add parameters enable_export_to_csv and
csv_file_name to the initialization of the grid:

csv_file_name is the name of the downloaded file. This parameter
is optional, if it is missing, the name of the grid is used instead. The
export icon will appear at the bottom right corner of the grid.

Next, each grid view helper should be placed in a partial of its own,
requiring it from the master template for the usual flow. There must be no
HTML or ERB code in this partial except for the grid helper.

By convention the name of such a partial follows the following pattern:

_GRID_NAME_grid.html.erb

In other words, a grid named tasks is expected to be found in a
template called _tasks_grid.html.erb (remember that the default
name of grids is 'grid'.)

Next, method export_grid_if_requested should be added to the end
of each action containing grids with enabled CSV export.

The naming convention for grid partials can be easily overridden by
supplying a hash parameter to export_grid_if_requested where each
key is the name of a grid, and the value is the name of the template (like
it is specified for render, i.e. without '_' and
extensions):

Detached Filters

It is possible to detach filters and place them anywhere on the page. To do
so, use parameter :detach_with_id for a column whose filter needs
to be detached, with an arbitrary string or a symbol value which will be
used later to identify the filter. As soon as there is one column with
:detach_with_id, the behavior of the grid helper changes
- it becomes an initializer of the grid and doesn't output any
HTML code. To render the grid, use grid for the second time
without the block. To render a detached output filter, use helper
grid_filter(grid_object, detached_filter_key):

It is important that the grid initializer goes first, the order of
grid_filter and the second call to grid is of no
importance.

Using custom submit and reset buttons together with :hide_submit_button
=> true and :hide_reset_button => true allows to
completely get rid of the default filter row and the default icons (see
section 'Submit/Reset Buttons').

For CSV export will continue functioning, just make sure the first call to
grid is still in the template of its own and is inside of
<%= %>, because when CSV is requested, the first
grid works in the old fashioned way producing CSV formatted
output.

This feature also works with :erb_mode => true.

If a column was declared with :detach_with_id, but never output
with grid_filter, filtering the grid in development mode will
result in an warning javascript message and the missing filter will be
ignored. There is no such message in production.

grid parameter :show_filter must not be set to
:no for detached filters to work.

How Does It Work? (For the interested)

When there is at least one column with :detach_with_id, the
generated HTML code is stored in a buffer, code for detached filters is
stored in buffers of their own identified by the given IDs, and nothing is
returned to the view. When the helper is called for the second time, the
buffer outputs its content. In a similar fashion, the grid_filter
helper outputs buffers for filters.

Compatibility with Rails Asset Caching

Helpers names_of_wice_grid_stylesheets and
names_of_wice_grid_javascripts return names of stylesheet and
javascript files and can be used with stylesheet_link_tag and
javascript_include_tag with :cache => true. Using this
trick you have to deal with the parameters correctly, mind that Prototype
has to be loaded before WiceGrid javascripts:

Because of the current implementation of WiceGrid these helpers work only
after the declaration of the grid in the view. This is due to the lazy
nature of WiceGrid - the actual call to the database is made during the
execution of the grid helper, because to build the correct query
columns declarations are required.

Accessing Records Via Callbacks

It is possible to set up callbacks which are executed from within the
plugin just after the call to the database. The callbacks are called before
rendering the grid cells, so the results of this processing can be used in
the grid. There are 3 ways you can set up such callbacks:

This lazy nature exists for performance reasons. Reading all records leads
to an additional call, and there can be cases when processing all records
should be triggered only under certain circumstances: