wice_grid 3.5.0 → 3.6.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.inch.yml +3 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +181 -0
  5. data/.travis.yml +22 -0
  6. data/{CHANGELOG → CHANGELOG.md} +95 -31
  7. data/Gemfile +4 -1
  8. data/README.md +1517 -0
  9. data/Rakefile +51 -7
  10. data/{SAVED_QUERIES_HOWTO.rdoc → SAVED_QUERIES_HOWTO.md} +34 -31
  11. data/TODO.md +16 -0
  12. data/lib/generators/wice_grid/add_migration_for_serialized_queries_generator.rb +4 -6
  13. data/lib/generators/wice_grid/install_generator.rb +2 -5
  14. data/lib/generators/wice_grid/templates/create_wice_grid_serialized_queries.rb +1 -0
  15. data/lib/generators/wice_grid/templates/wice_grid_config.rb +29 -34
  16. data/lib/wice/active_record_column_wrapper.rb +36 -17
  17. data/lib/wice/columns.rb +53 -52
  18. data/lib/wice/columns/column_action.rb +11 -13
  19. data/lib/wice/columns/column_boolean.rb +9 -11
  20. data/lib/wice/columns/column_bootstrap_datepicker.rb +48 -0
  21. data/lib/wice/columns/column_custom_dropdown.rb +22 -23
  22. data/lib/wice/columns/column_float.rb +2 -6
  23. data/lib/wice/columns/column_html5_datepicker.rb +31 -0
  24. data/lib/wice/columns/column_integer.rb +9 -13
  25. data/lib/wice/columns/column_jquery_datepicker.rb +49 -0
  26. data/lib/wice/columns/column_processor_index.rb +18 -13
  27. data/lib/wice/columns/column_rails_date_helper.rb +41 -0
  28. data/lib/wice/columns/column_rails_datetime_helper.rb +40 -0
  29. data/lib/wice/columns/column_range.rb +7 -11
  30. data/lib/wice/columns/column_string.rb +24 -20
  31. data/lib/wice/columns/common_date_datetime_mixin.rb +20 -0
  32. data/lib/wice/columns/common_js_date_datetime_conditions_generator_mixin.rb +39 -0
  33. data/lib/wice/columns/common_js_date_datetime_mixin.rb +15 -0
  34. data/lib/wice/columns/{column_date.rb → common_rails_date_datetime_conditions_generator_mixin.rb} +4 -22
  35. data/lib/wice/columns/common_standard_helper_date_datetime_mixin.rb +22 -0
  36. data/lib/wice/grid_output_buffer.rb +19 -10
  37. data/lib/wice/grid_renderer.rb +146 -85
  38. data/lib/wice/helpers/bs_calendar_helpers.rb +6 -7
  39. data/lib/wice/helpers/js_calendar_helpers.rb +19 -17
  40. data/lib/wice/helpers/wice_grid_misc_view_helpers.rb +18 -18
  41. data/lib/wice/helpers/wice_grid_serialized_queries_view_helpers.rb +44 -49
  42. data/lib/wice/helpers/wice_grid_view_helpers.rb +131 -134
  43. data/lib/wice/kaminari_monkey_patching.rb +3 -1
  44. data/lib/wice/table_column_matrix.rb +23 -8
  45. data/lib/wice/wice_grid_controller.rb +12 -16
  46. data/lib/wice/wice_grid_core_ext.rb +12 -20
  47. data/lib/wice/wice_grid_misc.rb +131 -53
  48. data/lib/wice/wice_grid_serialized_queries_controller.rb +10 -11
  49. data/lib/wice/wice_grid_serialized_query.rb +4 -3
  50. data/lib/wice/wice_grid_spreadsheet.rb +19 -18
  51. data/lib/wice_grid.rb +144 -135
  52. data/spec/schema.rb +9 -0
  53. data/spec/spec_helper.rb +75 -0
  54. data/spec/support/active_record.rb +11 -0
  55. data/spec/support/wice_grid_test_config.rb +172 -0
  56. data/spec/wice/grid_output_buffer_spec.rb +41 -0
  57. data/spec/wice/table_column_matrix_spec.rb +38 -0
  58. data/spec/wice/wice_grid_misc_spec.rb +159 -0
  59. data/spec/wice/wice_grid_spreadsheet_spec.rb +14 -0
  60. data/test/readme.txt +1 -1
  61. data/vendor/assets/javascripts/wice_grid_init.js.coffee +14 -8
  62. data/vendor/assets/stylesheets/wice_grid.scss +84 -0
  63. data/wice_grid.gemspec +32 -16
  64. metadata +217 -25
  65. data/README.rdoc +0 -1325
  66. data/lib/generators/wice_grid/templates/wice_grid.scss +0 -140
  67. data/lib/wice/columns/column_datetime.rb +0 -171
  68. data/vendor/assets/images/icons/grid/arrow_down.gif +0 -0
  69. data/vendor/assets/images/icons/grid/arrow_up.gif +0 -0
  70. data/vendor/assets/images/icons/grid/calendar_view_month.png +0 -0
  71. data/vendor/assets/images/icons/grid/collapse.gif +0 -0
  72. data/vendor/assets/images/icons/grid/delete.png +0 -0
  73. data/vendor/assets/images/icons/grid/expand.gif +0 -0
  74. data/vendor/assets/images/icons/grid/page_white_excel.png +0 -0
  75. data/vendor/assets/images/icons/grid/page_white_find.png +0 -0
  76. data/vendor/assets/images/icons/grid/table.png +0 -0
  77. data/vendor/assets/images/icons/grid/table_refresh.png +0 -0
  78. data/vendor/assets/images/icons/grid/tick_all.png +0 -0
  79. data/vendor/assets/images/icons/grid/untick_all.png +0 -0
data/Gemfile CHANGED
@@ -1 +1,4 @@
1
- gemspec
1
+ # encoding: utf-8
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,1517 @@
1
+ [![Version](http://img.shields.io/gem/v/wice_grid.svg)](https://rubygems.org/gems/wice_grid)
2
+ [![Build](https://travis-ci.org/leikind/wice_grid.svg)](https://travis-ci.org/leikind/wice_grid)
3
+ [![Inline docs](http://inch-ci.org/github/leikind/wice_grid.svg?branch=rails3)](http://inch-ci.org/github/leikind/wice_grid/branch/rails3)
4
+ [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
5
+
6
+ <!-- let's disable for a while ;-)
7
+ [![Coverage Status](https://coveralls.io/repos/leikind/wice_grid/badge.svg?branch=development&service=github)](https://coveralls.io/github/leikind/wice_grid?branch=development)
8
+ -->
9
+
10
+ * Yuri Leikind, yuri.leikind at gmail dot com
11
+ * Version 3.6.0.pre1
12
+ * This tutorial is accompanied by a sample application with WiceGrid examples which you can browse online:
13
+ http://wicegrid.herokuapp.com, or just view the code: https://github.com/leikind/wice_grid_testbed.
14
+
15
+
16
+
17
+ # WiceGrid
18
+
19
+ - [Intro](#intro)
20
+ - [Requirements and Rails versions](#requirements-and-rails-versions)
21
+ - [Installation](#installation)
22
+ - [Basics](#basics)
23
+ - [Rendering filter panel](#rendering-filter-panel)
24
+ - [Initial Ordering](#initial-ordering)
25
+ - [Records Per Page](#records-per-page)
26
+ - [Conditions](#conditions)
27
+ - [Queries with join tables](#queries-with-join-tables)
28
+ - [Joined associations referring to the same table](#joined-associations-referring-to-the-same-table)
29
+ - [More than one grid on a page](#more-than-one_grid-on-a-page)
30
+ - [Custom Ordering](#custom-ordering)
31
+ - [Filters](#filters)
32
+ - [Custom dropdown filters](#custom-dropdown-filters)
33
+ - [Numeric Filters](#numeric-filters)
34
+ - [Date and DateTime Filters](#date-and-datetime-filters)
35
+ - [Detached Filters](#detached-filters)
36
+ - [Defaults](#defaults)
37
+ - [Bug reports](#bug-reports)
38
+
39
+
40
+ ## Intro
41
+
42
+ WiceGrid is a Rails grid plugin.
43
+
44
+ One of the goals of this plugin was to allow the programmer to define the contents of the cell on their
45
+ own, just like one does when rendering a collection via a simple table (and this is what differentiates
46
+ WiceGrid from various scaffolding solutions), but automate implementation of filters, ordering,
47
+ paginations, CSV export, and so on. Ruby blocks provide an elegant means for this.
48
+
49
+
50
+ WiceGrid builds the call to the ActiveRecord layer for you and creates a table view with the results
51
+ of the call including:
52
+
53
+ * Pagination
54
+ * Sortable columns
55
+ * Filtering by multiple columns
56
+ * Export to CSV
57
+ * Saved queries
58
+
59
+ Filters are added automatically according to the type of the underlying DB column. Filtering by more
60
+ than one column at the same time is possible. More than one such grid can appear on a page, and
61
+ manipulations with one grid do not have any impact on others.
62
+
63
+ WiceGrid does not take a collection as an input, it works directly with ActiveRecord.
64
+
65
+ WiceGrid does not use XHR calls to reload itself, instead simple GET requests are used for this,
66
+ nevertheless, all other page parameters are respected and preserved. WiceGrid works well with Turbolinks.
67
+
68
+ WiceGrid views do not contain forms so you can include it in your own forms.
69
+
70
+ WiceGrid is known to work with MySQL, Postgres, and Oracle.
71
+
72
+
73
+ ## Requirements and Rails versions
74
+
75
+ For rails 2 use version 0.6 in [the master branch](https://github.com/leikind/wice_grid/tree/master).
76
+ That branch is hardly supported.
77
+
78
+ The main supported branch is `rails3`. Latest Rails 3.2.x and Rails 4.x.x are supported in this branch.
79
+
80
+ If you need to use the plugin in with Rails 3.0.x and 3.1.x versions, please use WiceGrid version 3.0.4.
81
+
82
+ WiceGrid relies on jQuery.
83
+
84
+ If you need a JS Datepicker, WiceGrid supports jQuery Datepicker or
85
+ [Bootstrap Datepicker](https://github.com/Nerian/bootstrap-datepicker-rails), so you might need one of
86
+ those. See section Installation for details on how to use datepickers.
87
+
88
+ WARNING: Since 3.2.pre2 WiceGrid is not compatible with `will_paginate` because internally it uses
89
+ `kaminari` for pagination, and `kaminari` is not compatible with `will_paginate`!
90
+
91
+
92
+ ## Installation
93
+
94
+ Add the following to your Gemfile:
95
+
96
+ ```
97
+ gem "wice_grid", '3.6.0.pre1'
98
+ ```
99
+
100
+ and run the `bundle` command.
101
+
102
+ Run the generator:
103
+
104
+ ```
105
+ rails g wice_grid:install
106
+ ```
107
+
108
+ This adds the following files:
109
+ * config/initializers/wice_grid_config.rb
110
+ * config/locales/wice_grid.yml
111
+ * app/assets/stylesheets/wice_grid.scss
112
+
113
+
114
+ Require WiceGrid javascript in your js index file:
115
+
116
+ ```
117
+ //= require wice_grid
118
+ ```
119
+
120
+ Make sure jQuery is loaded. If the application uses Date and DateTime filters, you have to install
121
+ jQuery Datepicker by yourself. You can also use
122
+ [Bootstrap Datepicker](https://github.com/Nerian/bootstrap-datepicker-rails).
123
+
124
+ Here is an example of `application.js` if jquery.ui.datepicker is used:
125
+
126
+ ```
127
+ //= require jquery
128
+ //= require jquery_ujs
129
+ //= require jquery-ui
130
+ //= require wice_grid
131
+ //= require jquery.ui.datepicker
132
+ //= require_tree .
133
+ ```
134
+
135
+ Here is `application.js` if [Bootstrap Datepicker](https://github.com/Nerian/bootstrap-datepicker-rails) is used:
136
+
137
+ ```
138
+ //= require jquery
139
+ //= require jquery_ujs
140
+ //= require jquery-ui
141
+ //= require wice_grid
142
+ //= require bootstrap-datepicker
143
+ //= require_tree .
144
+ ```
145
+
146
+ Require WiceGrid CSS in your `application.scss`:
147
+
148
+ ```
149
+ @import "wice_grid";
150
+ ```
151
+
152
+ This will provide very basic styles, not specifying exactly how the table should look like, but if
153
+ the application uses Twitter Bootstrap, the markup generated by WiceGrid will have correct classes and
154
+ will fit nicely.
155
+
156
+ WiceGrid uses icons from [Font Awesome](http://fortawesome.github.io/Font-Awesome/)
157
+
158
+ Should you decide to write you own styles for WiceGrid, the suggested way is to copy wice_grid.scss
159
+ from the plugin into `app/assets/stylesheets`, rename it to avoid loading name clashes, and replace
160
+ `@import "wice_grid";` by a directive to load your own SASS file.
161
+
162
+
163
+ ## Basics
164
+
165
+ The simplest example of a WiceGrid for one simple DB table is the following:
166
+
167
+ Controller:
168
+
169
+ ```ruby
170
+ @tasks_grid = initialize_grid(Task)
171
+ ```
172
+
173
+ It is also possible to use an `ActiveRecord::Relation` instance as the first argument:
174
+
175
+ ```ruby
176
+ @tasks_grid = initialize_grid(Task.where(active: true))
177
+ ```
178
+
179
+ View:
180
+
181
+ ```erb
182
+ <%= grid(@tasks_grid) do |g|
183
+
184
+ g.column do |task|
185
+ task.id
186
+ end
187
+
188
+ g.column do |task|
189
+ task.title
190
+ end
191
+
192
+ g.column do |task|
193
+ task.description
194
+ end
195
+
196
+ g.column do |task|
197
+ task.archived? ? 'Yes' : 'No'
198
+ end
199
+
200
+ g.column do |task|
201
+ link_to('Edit', edit_task_path(task))
202
+ end
203
+ end -%>
204
+ ```
205
+
206
+ Code `g.column do |task| ... end`
207
+ defines everything related to a column in the resulting view table including column names, sorting,
208
+ filtering, the content of the column cells, etc. The return value of the block is the table cell content.
209
+
210
+ Column names are defined with parameter `:name`:
211
+
212
+ ```erb
213
+ <%= grid(@tasks_grid) do |g|
214
+
215
+ g.column name: 'ID' do |task|
216
+ task.id
217
+ end
218
+
219
+ g.column name: 'Title' do |task|
220
+ task.title
221
+ end
222
+
223
+ g.column name: 'Description' do |task|
224
+ task.description
225
+ end
226
+
227
+ g.column name: 'Archived' do |task|
228
+ task.archived? ? 'Yes' : 'No'
229
+ end
230
+
231
+ g.column do |task|
232
+ link_to('Edit', edit_task_path(task))
233
+ end
234
+ end -%>
235
+ ```
236
+
237
+ To add filtering and ordering, declare to which column in the underlying database table(s) the view
238
+ column corresponds using parameter `:attribute`:
239
+
240
+ ```erb
241
+ <%= grid(@tasks_grid) do |g|
242
+
243
+ g.column name: 'ID', attribute: 'id' do |task|
244
+ task.id
245
+ end
246
+
247
+ g.column name: 'Title', attribute: 'title' do |task|
248
+ task.title
249
+ end
250
+
251
+ g.column name: 'Description', attribute: 'description' do |task|
252
+ task.description
253
+ end
254
+
255
+ g.column name: 'Archived', attribute: 'archived' do |task|
256
+ task.archived? ? 'Yes' : 'No'
257
+ end
258
+
259
+ g.column do |task|
260
+ link_to('Edit', edit_task_path(task))
261
+ end
262
+ end -%>
263
+ ```
264
+
265
+ This will add sorting links and filters for columns `Username` and `Active`. The plugin automatically
266
+ creates filters according to the type of the database column. In the above example a text field will be
267
+ created for column Title (title is a string), for column `Archived` a dropdown filter will be created
268
+ with options 'Yes', 'No', and '--', and for the integer ID two short text fields are added which can
269
+ contain the numeric range (more than, less than).
270
+
271
+ It is important to remember that `:attribute` is the name of the database column, not a model attribute.
272
+ Of course, all database columns have corresponding model attributes, but not all model attributes map to
273
+ columns in the same table with the same name.
274
+
275
+ Read more about available filters in the documentation for the column method.
276
+
277
+ Read the section about custom dropdown filters for more advanced filters.
278
+
279
+ For columns like
280
+
281
+ ```ruby
282
+ g.column name: 'Title', attribute: 'title' do |task|
283
+ task.title
284
+ end
285
+ ```
286
+
287
+ where the block contains just a call to the same attribute declared by :attribute, the block can be
288
+ omitted:
289
+
290
+ ```erb
291
+ <%= grid(@tasks_grid) do |g|
292
+
293
+ g.column name: 'ID', attribute: 'id'
294
+
295
+ g.column name: 'Title', attribute: 'title'
296
+
297
+ g.column name: 'Description', attribute: 'description'
298
+
299
+ g.column name: 'Archived', attribute: 'archived' do |task|
300
+ task.archived? ? 'Yes' : 'No'
301
+ end
302
+
303
+ g.column do |task|
304
+ link_to('Edit', edit_task_path(task))
305
+ end
306
+ end -%>
307
+ ```
308
+
309
+ In this case `name` will be used as the method name to send to the ActiveRecord instance.
310
+
311
+ If only ordering is needed, and no filter, we can turn off filters using `:filter` :
312
+
313
+ ```ruby
314
+ g.column name: 'ID', attribute: 'id', filter: false
315
+ ```
316
+
317
+ If no ordering links are needed, use `ordering: false`:
318
+
319
+ ```ruby
320
+ g.column name: 'Added', attribute: 'created_at', ordering: false
321
+ ```
322
+
323
+ It is important to understand that it is up to the developer to make sure that the value returned by a
324
+ column block (the content of a cell) corresponds to the underlying database column specified by
325
+ `:attribute` (and `:assoc` discussed below).
326
+
327
+
328
+ ### Rendering filter panel
329
+
330
+ The filter panel can be shown and hidden clicking the icon with binoculars.
331
+
332
+ The way the filter panel is shown after the page is loaded is controlled via parameter
333
+ `:show_filters` of the `grid` helper.
334
+ Possible values are:
335
+
336
+ * `:when_filtered` - the filter is shown when the current table is the result of filtering
337
+ * `:always` - always show the filter
338
+ * `:no` - never show the filter
339
+
340
+ Example:
341
+
342
+ ```erb
343
+ <%= grid(@tasks_grid, show_filters: :always) do |g|
344
+ ......
345
+ end -%>
346
+ ```
347
+
348
+ Filter related icons (filter icon, reset icon, show/hide icon) are placed in the header of the last
349
+ column if it doesn't have any filter or a column name, otherwise an additional table column is added.
350
+ To always place the icons in the additional column, set
351
+ `Wice::Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS` to `false` in the configuration file.
352
+
353
+
354
+ ### Initial ordering
355
+
356
+ Initializing the grid we can also define the column by which the record will be ordered <em>on the first
357
+ rendering of the grid</em>, when the user has not set their ordering setting by clicking the column label,
358
+ and the order direction:
359
+
360
+ ```ruby
361
+ @tasks_grid = initialize_grid(Task,
362
+ order: 'tasks.title',
363
+ order_direction: 'desc'
364
+ )
365
+ ```
366
+
367
+ ### Records per page
368
+
369
+ The number of rows per page is set with `:per_page`:
370
+
371
+ ```ruby
372
+ @tasks_grid = initialize_grid(Task, per_page: 40)
373
+ ```
374
+
375
+ ### Conditions
376
+
377
+ The `initialize_grid` method supports a `:conditions` parameter which is passed on to the underlying
378
+ ActiveRecord, so it can be in any format processable by ActiveRecord:
379
+
380
+ ```ruby
381
+ @tasks_grid = initialize_grid(Task,
382
+ conditions: ["archived = false and estimated_time > ?", 100]
383
+ )
384
+
385
+ @tasks_grid = initialize_grid(Task,
386
+ include: :project,
387
+ conditions: {archived: false, project: {active: true}}
388
+ )
389
+ ```
390
+
391
+ A good example is substituting a common pattern like
392
+
393
+ ```ruby
394
+ @user_groups = @portal_application.user_groups
395
+ ```
396
+
397
+ with WiceGrid code:
398
+
399
+ ```ruby
400
+ @user_groups_grid = initialize_grid(
401
+ UserGroup,
402
+ conditions: ['portal_application_id = ?', @portal_application]
403
+ )
404
+ ```
405
+
406
+ Alternatively, instead of a Class object as the first parameter, you can use ActiveRecord::Relation:
407
+
408
+ ```ruby
409
+ @tasks_grid = initialize_grid(
410
+ Task.where(archived: false, projects: {active: true}).joins(:project)
411
+ )
412
+ ```
413
+
414
+ Please note that though all queries inside of WiceGrid are run without the default scope, if you use an
415
+ ActiveRecord::Relation instance to initialize grid, it will already include the default scope. Thus you
416
+ might consider using `unscoped`:
417
+
418
+ ```ruby
419
+ @tasks_grid = initialize_grid(
420
+ Task.unscoped.where(archived: false, projects: {active: true}).joins(:project)
421
+ )
422
+ ```
423
+
424
+ ### Queries with join tables
425
+
426
+ To join other tables, use `:include`:
427
+
428
+ ```ruby
429
+ @products_grid = initialize_grid(Product,
430
+ include: :category,
431
+ order: 'products.name',
432
+ per_page: 20
433
+ )
434
+ ```
435
+
436
+ The value of `:include` can be an array of association names:
437
+
438
+ ```ruby
439
+ include: [:category, :users, :status]
440
+ ```
441
+
442
+ If you need to join tables to joined tables, use hashes:
443
+
444
+
445
+ ```ruby
446
+ include: [:category, {users: :group}, :status]
447
+ ```
448
+
449
+
450
+ Note that if we want to order initially by a column from a joined table we have to specify the table and
451
+ the column name with the sql dot notation, that is, `products.name`.
452
+
453
+ To show columns of joined tables in the view table, specify the corresponding association with `:assoc`:
454
+
455
+ ```erb
456
+ <%= grid(@products_grid) do |g|
457
+ g.column name: 'Product Name', attribute: 'name' do |product| # primary table
458
+ link_to(product.name, product_path(product))
459
+ end
460
+
461
+ g.column name: 'Category', attribute: 'name', assoc: :category do |product| # joined table
462
+ product.category.name
463
+ end
464
+ %>
465
+ ```
466
+
467
+ Please note that the blockless definition of the column can also be used with joined tables:
468
+
469
+ ```
470
+ g.column name: 'Category', attribute: 'name', assoc: :category
471
+
472
+ ```
473
+
474
+ If an association is mentioned in the column definition, it can be omitted from `:include` in `initialize_grid`.
475
+ Thus, the above example can be rewritten without `:category` in `:include`:
476
+
477
+
478
+ ```ruby
479
+ @products_grid = initialize_grid(Product,
480
+ order: 'products.name',
481
+ per_page: 20
482
+ )
483
+ ```
484
+
485
+ ```erb
486
+ <%= grid(@products_grid) do |g|
487
+ g.column name: 'Product Name', attribute: 'name' do |product| # primary table
488
+ link_to(product.name, product_path(product))
489
+ end
490
+
491
+ g.column name: 'Category', attribute: 'name', assoc: :category
492
+
493
+ %>
494
+ ```
495
+
496
+ ### Joined associations referring to the same table
497
+
498
+ In case there are two joined associations both referring to the same table, ActiveRecord constructs a query
499
+ where the second join provides an alias for the joined table. To enable WiceGrid to order and filter by
500
+ columns belonging to different associations but originating from the same table, set `:table_alias`
501
+ to this alias:
502
+
503
+ Model:
504
+
505
+ ```ruby
506
+ class Project < ActiveRecord::Base
507
+ belongs_to :customer, class_name: 'Company'
508
+ belongs_to :supplier, class_name: 'Company'
509
+ end
510
+ ```
511
+
512
+ Controller:
513
+
514
+ ```ruby
515
+ @projects_grid = initialize_grid(Project)
516
+ ```
517
+
518
+ View:
519
+
520
+ ```erb
521
+ <%= grid(@projects_grid, show_filters: :always) do |g|
522
+
523
+ g.column name: 'Project Name', attribute: 'name'
524
+
525
+ g.column name: 'Customer company', assoc: :customer, attribute: 'name'
526
+
527
+ g.column name: 'Supplier company', assoc: :supplier, attribute: 'name', table_alias: 'suppliers_projects'
528
+
529
+ end -%>
530
+ ```
531
+
532
+ ### More than one grid on a page
533
+
534
+ It is possible to use more that one grid on a page, each with its own state. To do so, you must specify the
535
+ name of the grid in `initialize_grid` using parameter `:name`.
536
+
537
+ The name serves as the base name for HTTP parameters, DOM IDs, etc, so it is important that all grids on a
538
+ page have different names. The default name is 'grid'.
539
+
540
+ The name can only contain alphanumeric characters.
541
+
542
+ ```ruby
543
+ @projects_grid = initialize_grid(Project, name: 'g1')
544
+ @tasks_grid = initialize_grid(Task, name: 'g2')
545
+ ```
546
+
547
+ ### Custom Ordering
548
+
549
+ It is possible to change the way results are ordered injecting a chunk of SQL code, for example, use
550
+ `ORDER BY INET_ATON(ip_address)` instead of `ORDER BY ip_address`.
551
+
552
+ To do so, provide parameter `:custom_order` in the initialization of the grid with a hash where
553
+ keys are fully qualified names of database columns, and values the required chunks of SQL to use in the
554
+ `ORDER BY` clause.
555
+
556
+ For example:
557
+
558
+ ```ruby
559
+ @hosts_grid = initialize_grid(Host,
560
+ custom_order: {
561
+ 'hosts.ip_address' => 'INET_ATON(hosts.ip_address)'
562
+ })
563
+ ```
564
+
565
+ It is possible to use the '?' character instead of the name of the column in the hash value:
566
+
567
+ ```ruby
568
+ @hosts_grid = initialize_grid(Host,
569
+ custom_order: {
570
+ 'hosts.ip_address' => 'INET_ATON( ? )'
571
+ })
572
+ ```
573
+
574
+ Values can also be Proc objects. The parameter supplied to such a Proc object is the name of the column:
575
+
576
+ ```ruby
577
+ @hosts_grid = initialize_grid(Host,
578
+ custom_order: {
579
+ 'hosts.ip_address' => lambda{|f| "INET_ATON( #{f} )"}
580
+ })
581
+ ```
582
+
583
+ ## Filters
584
+
585
+ Each column filter type is supported by a `column processor`. Each `column processor` is
586
+ responsible for
587
+
588
+ * generating HTML and supporting Javascript for the filter, input fields, dropdowns, javascript calendars, etc
589
+ * converting HTTP parameters from those input fields into ActiveRelation instances
590
+
591
+ By default column filters depend on the type of the underlying database column.
592
+
593
+ You can override these defaults in two ways:
594
+
595
+ * defining a custom filter with `:custom_filter`. Read more about it section "Custom dropdown filters".
596
+ * overriding the `column processor` type with `:filter_type`.
597
+
598
+ Which Column Processor is instantiated for which data types is defined in file
599
+ `lib/wice/columns/column_processor_index.rb`:
600
+
601
+ ```ruby
602
+ module Wice
603
+ module Columns
604
+ COLUMN_PROCESSOR_INDEX = ActiveSupport::OrderedHash[ #:nodoc:
605
+ :action, 'column_action', # Special processor for action column, columns with checkboxes
606
+ :text, 'column_string',
607
+ :string, 'column_string',
608
+ :rails_datetime_helper, 'column_rails_datetime_helper', # standard Rails datepicker helper
609
+ :rails_date_helper, 'column_rails_date_helper', # standard Rails date helper
610
+ :jquery_datepicker, 'column_jquery_datepicker',
611
+ :bootstrap_datepicker, 'column_bootstrap_datepicker',
612
+ :html5_datepicker, 'column_html5_datepicker', # not ready
613
+ :integer, 'column_integer',
614
+ :range, 'column_range',
615
+ :float, 'column_float',
616
+ :decimal, 'column_float',
617
+ :custom, 'column_custom_dropdown', # Special processor for custom filter columns
618
+ :boolean, 'column_boolean'
619
+ ]
620
+ end
621
+ end
622
+ ```
623
+
624
+ A good example for using `:filter_type` to change th default is numeric columns. By default
625
+ `'column_integer'` is instantiated for `integer` columns, and it renders one input field.
626
+ But it is also possible to use another Column Processor called `'column_range'` which renders two
627
+ input fields and searches for values in the given the range instead of searching for values which equal
628
+ the given search term.
629
+
630
+ It also possible to define and use your own column processors outside of the plugin, in you application.
631
+ Read more about this in section "Defining your own external filter processors".
632
+
633
+
634
+ ### Custom dropdown filters
635
+
636
+ It is possible to construct custom dropdown filters. A custom dropdown filter is essentially a dropdown
637
+ list.
638
+
639
+ Depending on the value of `column` parameter`:custom_filter` different modes are available:
640
+
641
+
642
+ #### Array of two-element arrays or a hash
643
+
644
+ An array of two-element arrays or a hash are semantically identical ways of creating a custom filter.
645
+
646
+ Every first item of the two-element array is used for the label of the select option while the second
647
+ element is the value of the select option. In case of a hash the keys become the labels of the generated
648
+ dropdown list, while the values will be values of options of the dropdown list:
649
+
650
+ ```ruby
651
+ g.column name: 'Status', attribute: 'status',
652
+ custom_filter: {'Development' => 'development', 'Testing' => 'testing', 'Production' => 'production'}
653
+
654
+ g.column name: 'Status', attribute: 'status',
655
+ custom_filter: [['Development', 'development'], ['Testing', 'testing'], ['Production', 'production']]
656
+ ```
657
+
658
+ It is also possible to submit a array of strings or numbers, in this case every item will be used both as
659
+ the value of the select option and as its label:
660
+
661
+ ```ruby
662
+ g.column name: 'Status', attribute: 'status', custom_filter: ['development', 'testing', 'production']
663
+ ```
664
+
665
+ #### :auto
666
+
667
+ `:auto` - a powerful option which populates the dropdown list with all unique values of the column
668
+ specified by `:attribute` and `:assoc`, if present.
669
+
670
+ ```ruby
671
+ g.column name: 'Status', attribute: 'status', custom_filter: :auto
672
+ ```
673
+
674
+ In the above example all statuses will appear in the dropdown even if they don't appear in the current
675
+ resultset.
676
+
677
+
678
+ #### Custom filters and associations (joined tables)
679
+
680
+ In most cases custom fields are needed for one-to-many and many-to-many associations.
681
+
682
+ To correctly build a filter condition foreign keys have to be used, not the actual values rendered in the
683
+ column.
684
+
685
+ For example, if there is a column:
686
+
687
+ ```ruby
688
+ g.column name: 'Project Name', attribute: 'name', assoc: :project do |task|
689
+ task.project.name if task.project
690
+ end
691
+ ```
692
+
693
+ adding `:custom_filter` like this:
694
+
695
+ ```ruby
696
+ g.column name: 'Project Name', attribute: 'name', assoc: :project,
697
+ custom_filter: Project.find(:all).map{|pr| [pr.name, pr.name]} do |task|
698
+ task.project.name if task.project
699
+ end
700
+ ```
701
+
702
+ is bad style and can fail, because the resulting condition will compare the name of the project,
703
+ `projects.name` to a string, and in some databases it is possible that different records
704
+ (projects in our example) have the same name.
705
+
706
+ To use filter with foreign keys, it is advised to change the declaration of the column from
707
+ `projects.name`, to `tasks.project_id`, and build the dropdown with foreign keys as values:
708
+
709
+ ```ruby
710
+ g.column name: 'Project Name', attribute: 'tasks.project_id',
711
+ custom_filter: Project.find(:all).map{|pr| [pr.id, pr.name]} do |task|
712
+ task.project.name if task.project
713
+ end
714
+ ```
715
+
716
+ However, this will break the ordering of the column - the column will be ordered by the integer foreign
717
+ key. To fix this, we can override the ordering using `:custom_order`:
718
+
719
+ ```ruby
720
+ @tasks_grid = initialize_grid(Task,
721
+ include: :project,
722
+ custom_order: {
723
+ 'tasks.project_id' => 'projects.name'
724
+ }
725
+ )
726
+ ```
727
+
728
+ #### Any other symbol (method name) or an array of symbols (method names)
729
+
730
+
731
+ For one symbol (different from `:auto`) the dropdown list is populated by all unique values returned
732
+ by the method with this name sent to <em>all</em> ActiveRecord objects throughout all pages.
733
+
734
+ The conditions set up by the user are ignored, that is, the records used are all those found on all pages
735
+ without any filters active.
736
+
737
+ For an array of symbols, the first method name is sent to the ActiveRecord object if it responds to this
738
+ method, the second method name is sent to the returned value unless it is `nil`, and so on. In other
739
+ words, a single symbol mode is the same as an array of symbols where the array contains just one element.
740
+
741
+ ```ruby
742
+ g.column name: 'Version', attribute: 'expected_version_id', custom_filter: [:expected_version, :to_option] do |task|
743
+ task.expected_version.name if task.expected_version
744
+ end
745
+ ```
746
+
747
+ There are two important differences from `:auto`:
748
+
749
+ 1. 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.
750
+ 2. Filtering by any option of such a custom filter will bring a non-empty list, unlike with `:auto`.
751
+
752
+
753
+ This mode has one major drawback - this mode requires an additional query without `offset` and `limit`
754
+ clauses to instantiate _all_ ActiveRecord objects, and performance-wise it brings all the advantages of
755
+ pagination to nothing. Thus, memory- and performance-wise this can be really bad for some queries and
756
+ tables and should be used with care.
757
+
758
+
759
+ If the final method returns a atomic value like a string or an integer, it is used for both the value and
760
+ the label of the select option element:
761
+
762
+ ```html
763
+ <option value="returned value">returned value</option>
764
+ ```
765
+
766
+ However, if the retuned value is a two element array, the first element is used for the option label and
767
+ the second - for the value.
768
+
769
+ Typically, a model method like the following:
770
+
771
+ ```ruby
772
+ def to_option
773
+ [name, id]
774
+ end
775
+ ```
776
+
777
+ together with
778
+
779
+ ```ruby
780
+ custom_filter: :to_option
781
+ ```
782
+
783
+ would do the trick:
784
+
785
+ ```html
786
+ <option value="id">name</option>
787
+ ```
788
+
789
+ Alternatively, a hash with the single key-value pair can be used, where the key will be used for the
790
+ label, and the key - for the value:
791
+
792
+ ```ruby
793
+ def to_option
794
+ {name => id}
795
+ end
796
+ ```
797
+
798
+ #### Special treatment of values 'null' and 'not null'
799
+
800
+ Values `null` and `not null` in a generated custom filter are treated specially, as SQL `null` statement
801
+ and not as strings. Value `null` is transformed into SQL condition `IS NULL`, and `not null` into
802
+ `IS NOT NULL`.
803
+
804
+ Thus, if in a filter defined by
805
+
806
+ ```ruby
807
+ custom_filter: {'No' => 'null', 'Yes' => 'not null', '1' => 1, '2' => '2', '3' => '3'}
808
+ ```
809
+
810
+ values '1', '2' and 'No' are selected (in a multi-select mode), this will result in the following SQL:
811
+
812
+ ```sql
813
+ ( table.field IN ( '1', '2' ) OR table.field IS NULL )
814
+ ```
815
+
816
+ #### Multiple selection
817
+
818
+ By default it is possible for any dropdown list to switch between single and multiple selection modes.
819
+ To only allow single selection use `:allow_multiple_selection`:
820
+
821
+ ```ruby
822
+ g.column name: 'Expected in version', attribute: 'expected_version_id',
823
+ custom_filter: [:expected_version, :to_option], allow_multiple_selection: false do |task|
824
+ ...
825
+ end
826
+ ```
827
+
828
+ ### Numeric Filters
829
+
830
+ Before version 3.2.1 the filter used for numeric columns was a range filter with two limits. Beginning
831
+ with version 3.2.1 the default is a direct comparison filter with one input field. The old range filter
832
+ can still be loaded using parameter `:filter_type` with value `:range`:
833
+
834
+ ```ruby
835
+ g.column filter_type: :range do |task|
836
+ ...
837
+ end
838
+ ```
839
+
840
+ ### Date and DateTime Filters
841
+
842
+ WiceGrid provides four filters for selecting dates and time:
843
+
844
+ * ```:jquery_datepicker``` - Jquery datepicker (works for datetime, too)
845
+ * ```:bootstrap_datepicker``` - Bootstrap datepicker (works for datetime, too)
846
+ * ```:rails_date_helper``` - standard Rails date helper
847
+ * ```:rails_datetime_helper``` - standard Rails datetime helper
848
+
849
+ Specify a date/datetime filter just like you specify any other filter:
850
+
851
+ ```
852
+ g.column name: 'Updated', attribute: 'updated_at', filter_type: :rails_datetime_helper do |task|
853
+ task.updated_at.to_s(:db)
854
+ end
855
+ ```
856
+
857
+ Default filters are defined in configuration constants Wice::Defaults::DEFAULT_FILTER_FOR_DATE and
858
+ Wice::Defaults::DEFAULT_FILTER_FOR_DATETIME.
859
+
860
+
861
+
862
+ #### jQuery UI DatePicker `(HELPER_STYLE = :calendar)`
863
+
864
+ By default WiceGrid uses jQuery UI datepicker[http://jqueryui.com/demos/datepicker/] for Date and DateTime
865
+ filters. Because this is part of the standard jQuery UI codebase, it is not bundled together with the
866
+ plugin, and it is the responsibility of the programmer to include all necessary assets including
867
+ localization files if the application is multilingual.
868
+
869
+ jQuery UI datepicker does not have any time related controls, and when dealing with DateTime filters, the
870
+ time value is ignored.
871
+
872
+ Constants `DATE_FORMAT` and `DATETIME_FORMAT` in the configuration file define the format of dates the
873
+ user will see, as well as the format of the string sent in a HTTP parameter. If you change the formats,
874
+ make sure that lamdbas defined in `DATETIME_PARSER` and `DATE_PARSER` return valid DateTime and Date
875
+ objects.
876
+
877
+ jQuery `datepicker` uses a different format flavor, therefore there is an additional constant
878
+ `DATE_FORMAT_JQUERY`. While `DATE_FORMAT_JQUERY` is fed to `datepicker`, `DATE_FORMAT` is still used
879
+ for presenting initial date values in filters, so make sure that `DATE_FORMAT_JQUERY` and `DATE_FORMAT`
880
+ result in an identical date representation.
881
+
882
+ Constant `DATEPICKER_YEAR_RANGE` defines the range of years in the Datepicker year dropdown. Alternatively,
883
+ you can always change this range dynamically with the following javascript:
884
+
885
+ ```js
886
+ $( ".hasDatepicker" ).datepicker( "option", "yearRange", "2000:2042" );
887
+ ```
888
+
889
+ #### jQuery UI DatePicker `(HELPER_STYLE = :bootstrap)`
890
+
891
+ WiceGrid also supports [Bootstrap Datepicker](https://github.com/Nerian/bootstrap-datepicker-rails).
892
+
893
+ #### Rails standard input fields `(HELPER_STYLE = :standard)`
894
+
895
+ Another option is standard Rails helpers for date fields, these are separate select fields for years,
896
+ months and days (also for hour and minute if it is a datetime field).
897
+
898
+ ### Detached Filters
899
+
900
+ Filters can also be detached from the grid table and placed anywhere on page.
901
+
902
+ This is a 3-step process.
903
+
904
+ First, define the grid with helper `define_grid` instead of `grid`. Everything should be done the same way
905
+ as with `grid`, but every column which will have an external filter, add
906
+ `detach_with_id: :some_filter_name`` in the column definition. The value of `:detach_with_id` is an
907
+ arbitrary string or a symbol value which will be used later to identify the filter.
908
+
909
+ ```erb
910
+ <%= define_grid(@tasks_grid, show_filters: :always) do |g|
911
+
912
+ g.column name: 'Title', attribute: 'title', detach_with_id: :title_filter do |task|
913
+ link_to('Edit', edit_task_path(task.title))
914
+ end
915
+
916
+ g.column name: 'Archived', attribute: 'archived', detach_with_id: :archived_filter do |task|
917
+ task.archived? ? 'Yes' : 'No'
918
+ end
919
+
920
+ g.column name: 'Added', attribute: 'created_at', detach_with_id: :created_at_filter do |task|
921
+ task.created_at.to_s(:short)
922
+ end
923
+
924
+ end -%>
925
+ ```
926
+
927
+ Then, use `grid_filter(grid, :some_filter_name)` to render filters:
928
+
929
+ ```erb
930
+ <% # rendering filter with key :title_filter %>
931
+ <%= grid_filter @tasks_grid, :title_filter %>
932
+
933
+ <% # rendering filter with key :archived_filter %>
934
+ <%= grid_filter @tasks_grid, :archived_filter %>
935
+
936
+ <% # rendering filter with key :created_at_filter %>
937
+ <%= grid_filter @tasks_grid, :created_at_filter %>
938
+
939
+ <% # Rendering the grid body %>
940
+ <%= grid(@tasks_grid) %>
941
+ ```
942
+
943
+ Finally, use `render_grid(@grid)` to actually output the grid table.
944
+
945
+
946
+ Using custom submit and reset buttons together with `hide_submit_button: true` and
947
+ `hide_reset_button: true` allows to completely get rid of the default filter row and the default
948
+ icons (see section 'Submit/Reset Buttons').
949
+
950
+
951
+ If a column was declared with `:detach_with_id`, but never output with `grid_filter`, filtering
952
+ the grid in development mode will result in an warning javascript message and the missing filter will be
953
+ ignored. There is no such message in production.
954
+
955
+
956
+ ### Defining your own external filter processors
957
+
958
+
959
+ It possible to define and use your own column processors outside of the plugin, in you application.
960
+
961
+ The first step is to edit `Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS` in
962
+ `wice_grid_config.rb`:
963
+
964
+ ```ruby
965
+
966
+ Wice::Defaults::ADDITIONAL_COLUMN_PROCESSORS = {
967
+ my_own_filter: ['ViewColumnMyOwnFilter', 'ConditionsGeneratorMyOwnFilter'],
968
+ another_filter: ['ViewColumnAnotherFilter', 'ConditionsGeneratorAnotherFilter']
969
+ }
970
+ ```
971
+
972
+ The first element in the two-item array is the name of a class responsible for rendering
973
+ the filter view. The second element is the name of a class responsible for processing
974
+ filter parameters.
975
+
976
+ For examples of these two classes look at the existing column processors in `lib/wice/columns/`
977
+
978
+ The structure of these two classes is as follows:
979
+
980
+ ```ruby
981
+ class ViewColumnMyOwnFilter < Wice::Columns::ViewColumn
982
+
983
+ def render_filter_internal(params)
984
+ ...
985
+ end
986
+
987
+ def yield_declaration_of_column_filter
988
+ {
989
+ templates: [...],
990
+ ids: [...]
991
+ }
992
+ end
993
+ end
994
+
995
+
996
+ class ConditionsGeneratorMyOwnFilter < Wice::Columns::ConditionsGeneratorColumn
997
+
998
+ def generate_conditions(table_name, opts)
999
+ ...
1000
+ end
1001
+
1002
+ end
1003
+ ```
1004
+
1005
+ To use an external column processor use `:filter_type` in a column definition:
1006
+
1007
+ ```ruby
1008
+ column name: 'name', attribute: 'attribute', filter_type: :my_own_filter do |rec|
1009
+ ...
1010
+ end
1011
+ ```
1012
+
1013
+ ## Defaults
1014
+
1015
+ Default values like can be changed in `config/initializers/wice_grid_config.rb`.
1016
+
1017
+ ## Submit/Reset buttons
1018
+ Instead of using default Submit and Reset icons you can use external HTML elements to trigger
1019
+ these actions. Add a button or any other clickable HTML element with class
1020
+ `wg-external-submit-button` or `wg-external-reset-button`, and attribute `data-grid-name`
1021
+ whose value is the name of the grid:
1022
+
1023
+ ```html
1024
+ <button class="wg-external-submit-button" data-grid-name="grid">Submit</button>
1025
+ <button class="wg-external-reset-button" data-grid-name="grid">Reset</button>
1026
+ ```
1027
+
1028
+ To hide the default icons use `hide_submit_button: true` and
1029
+ `hide_reset_button: true` in the `grid` helper.
1030
+
1031
+
1032
+ ## Auto-reloading filters
1033
+
1034
+ It is possible to configure a grid to reload itself once a filter has been changed. It works with all
1035
+ filter types including the JS calendar, the only exception is the standard Rails date/datetime filters.
1036
+
1037
+ Use option `:auto_reload` in the column definiton:
1038
+
1039
+ ```erb
1040
+
1041
+ <%= grid(@tasks_grid, show_filters: :always, hide_submit_button: true) do |g|
1042
+
1043
+ # String
1044
+ g.column name: 'Title', attribute: 'title', auto_reload: true
1045
+
1046
+ # Boolean
1047
+ g.column name: 'Archived', attribute: 'archived', auto_reload: true
1048
+
1049
+ # Custom (dropdown)
1050
+ g.column name: 'Status', attribute: 'status_id', custom_filter: Status.to_dropdown, auto_reload: true do |task|
1051
+ task.status.name if task.status
1052
+ end
1053
+
1054
+ # Datetime
1055
+ g.column name: 'Added', attribute: 'created_at', auto_reload: true, helper_style: :calendar do |task|
1056
+ task.created_at.to_s(:short)
1057
+ end
1058
+
1059
+ end -%>
1060
+ ```
1061
+
1062
+ To make this behavior default change constant `AUTO_RELOAD` in the configuration file.
1063
+
1064
+ ## Styling the grid
1065
+
1066
+
1067
+ ### Adding classes and styles
1068
+
1069
+ The `grid` helper accepts parameter `:html` which is a hash of HTML attributes for the table tag.
1070
+
1071
+ Another `grid` parameter is `header_tr_html` which is a hash of HTML attributes to
1072
+ be added to the first `tr` tag (or two first `tr`'s if the filter row is present).
1073
+
1074
+ `:html` is a parameter for the `column` method setting HTML attributes of `td` tags for a certain column.
1075
+
1076
+ ### Adding classes and styles dynamically
1077
+
1078
+ WiceGrid offers ways to dynamically add classes and styles to `TR` and `TD` based on the current ActiveRecord instance.
1079
+
1080
+
1081
+ For `<TD>`, let the `column` return an array where the first item is the usual
1082
+ string output whole the second is a hash of HTML attributes to be added for the
1083
+ `<td>` tag of the current cell:
1084
+
1085
+ ```ruby
1086
+ g.column do |portal_application|
1087
+ css_class = portal_application.public? ? 'public' : 'private'
1088
+ [portal_application.name, {class: css_class}]
1089
+ end
1090
+ ```
1091
+
1092
+ For adding classes/styles to `<TR>` use special clause `row_attributes` ,
1093
+ similar to `column`, only returning a hash:
1094
+
1095
+ ```erb
1096
+ <%= grid(@versions_grid) do |g|
1097
+ g.row_attributes do |version|
1098
+ if version.in_production?
1099
+ {style: 'background-color: rgb(255, 255, 204);'}
1100
+ end
1101
+ end
1102
+
1103
+ g.column{|version| ... }
1104
+ g.column{|version| ... }
1105
+ end -%>
1106
+ ```
1107
+
1108
+ Naturally, there can be only one `row_attributes` definition for a WiceGrid instance.
1109
+
1110
+ Various classes do not overwrite each other, instead, they are concatenated.
1111
+
1112
+
1113
+ ## Adding rows to the grid
1114
+
1115
+ It is possible to add your own handcrafted HTML after and/or before each grid row.
1116
+ This works similar to `row_attributes`, by adding blocks `after_row`, `before_row`, and `last_row`:
1117
+
1118
+ ```erb
1119
+ <%= grid(@tasks_grid) do |g|
1120
+ g.before_row do |task, number_of_columns|
1121
+ if task.active?
1122
+ "<tr><td colspan=\"10\">Custom line for #{t.name}</td></tr>" # this would add a row
1123
+ # before every active task row
1124
+ else
1125
+ nil
1126
+ end
1127
+ end
1128
+
1129
+ g.last_row do |number_of_columns| # This row will always be added to the bottom of the grid
1130
+ content_tag(:tr,
1131
+ content_tag(:td,
1132
+ 'Last row',
1133
+ colspan: 10),
1134
+ class: 'last_row')
1135
+ end
1136
+
1137
+ .......
1138
+ end %>
1139
+ ```
1140
+
1141
+ It is up for the developer to return the correct HTML code, or return `nil` if no row is needed for this record.
1142
+ Naturally, there is only one `before_row` definition and one `after_row` definition for a WiceGrid instance.
1143
+
1144
+ The second variable injected into to `before_row` and `after_row` block, and the first parameter injected
1145
+ into the `last_row` is the number of columns in the current grid.
1146
+
1147
+ ## Rendering a grid without records
1148
+
1149
+ If the grid does not contain any records to show, it is possible show some alternative view instead of
1150
+ an empty grid. Bear in mind that if the user sets up the filters in such a way that the selection of
1151
+ records is empty, this will still render the grid and it will be possible to reset the grid clicking
1152
+ on the Reset button. Thus, this only works if the initial number of records is 0.
1153
+
1154
+ ```erb
1155
+ <%= grid(@grid) do |g|
1156
+
1157
+ g.blank_slate do
1158
+ "There are no records"
1159
+ end
1160
+
1161
+ g.column do |product|
1162
+ ...
1163
+ end
1164
+ end -%>
1165
+ ```
1166
+
1167
+ There are two alternative ways to do the same, submitting a string to `blank_slate`:
1168
+
1169
+ ```ruby
1170
+ g.blank_slate "some text to be rendered"
1171
+ ```
1172
+
1173
+ Or a partial:
1174
+
1175
+ ```ruby
1176
+ g.blank_slate partial: "partial_name"
1177
+ ```
1178
+
1179
+ ## Action Column
1180
+
1181
+ It is possible to add a column with checkboxes for each record. This is useful for actions with multiple records,
1182
+ for example, deleting selected records. Please note that `action_column` only creates the checkboxes and the
1183
+ 'Select All' and 'Deselect All' buttons, and the form itself as well as processing the parameters should be
1184
+ taken care of by the application code.
1185
+
1186
+ ```erb
1187
+ <%= grid(@tasks_grid, show_filters: :always) do |g|
1188
+
1189
+ ...
1190
+
1191
+ g.action_column
1192
+
1193
+ ...
1194
+
1195
+ end -%>
1196
+ ```
1197
+
1198
+ By default the name of the HTTP parameter follows pattern `"#{grid_name}[#{param_name}][]"`, thus
1199
+ `params[grid_name][param_name]` will contain an array of object IDs.
1200
+
1201
+ You can hide a certain action checkbox if you add the usual block to `g.action_column`, just like with the
1202
+ `g.column` definition. If the block returns `nil` or `false` no checkbox will be rendered.
1203
+
1204
+ ```erb
1205
+ <%= grid(@tasks_grid, show_filters: :always) do |g|
1206
+
1207
+ ...
1208
+
1209
+ g.action_column do |task|
1210
+ task.finished?
1211
+ end
1212
+
1213
+ ...
1214
+
1215
+ end -%>
1216
+ ```
1217
+
1218
+ WiceGrid is form-friendly: submitting grid in a form retains the state of the form.
1219
+
1220
+
1221
+
1222
+ ## Integration of the grid with other forms on page
1223
+
1224
+ Imagine that the user should be able to change the behavior of the grid using some other control
1225
+ on the page, and not a grid filter.
1226
+
1227
+ For example, on a page showing tasks, change between 'Show active tasks' to 'Show archived tasks' using a dropdown box.
1228
+ WiceGrid allows to keep the status of the grid with all the filtering and sorting using helper
1229
+ `dump_filter_parameters_as_hidden_fields` which takes a grid object and dumps
1230
+ all current sorting and filtering parameters as hidden fields. Just include
1231
+ `dump_filter_parameters_as_hidden_fields(@grid)` inside your form, and the newly rendered grid will keep ordering and filtering.
1232
+
1233
+ ```erb
1234
+ <% form_tag('', method: :get) do %>
1235
+ <%= dump_filter_parameters_as_hidden_fields(@tasks_grid) %>
1236
+ <%= select_tag 'archived',
1237
+ options_for_select([['View active tasks', 0], ['View archived tasks', 1]], @archived ? 1 : 0),
1238
+ onchange: 'this.form.submit()' %>
1239
+ <% end -%>
1240
+ ```
1241
+
1242
+
1243
+ ## Show All Records
1244
+
1245
+ It is possible to switch to the All Records mode clicking on link "show all" in the bottom right corner.
1246
+ This functionality should be used with care. To turn this mode off for all grid instances,
1247
+ change constant `ALLOW_SHOWING_ALL_RECORDS` in `config/initializers/wice_grid_config.rb` to
1248
+ `false`. To do so for a specific grid, use initializer parameter `:allow_showing_all_records`.
1249
+
1250
+ Configuration constant `START_SHOWING_WARNING_FROM` sets the threshold number of all records after
1251
+ which clicking on the link results in a javascript confirmation dialog.
1252
+
1253
+
1254
+ ## CSV Export
1255
+
1256
+ It is possible to export the data displayed on a grid to a CSV file. The dumped data is the current resultset
1257
+ with all the current filters and sorting applied, only without the pagination constraint (i.e. all pages).
1258
+
1259
+ To enable CSV export add parameters `enable_export_to_csv` and `csv_file_name` to the initialization of the grid:
1260
+
1261
+ ```ruby
1262
+ @projects_grid = initialize_grid(Project,
1263
+ include: [:customer, :supplier],
1264
+ name: 'g2',
1265
+ enable_export_to_csv: true,
1266
+ csv_file_name: 'projects'
1267
+ )
1268
+ ```
1269
+
1270
+ `csv_file_name` is the name of the downloaded file. This parameter is optional, if it is missing, the name of
1271
+ the grid is used instead. The export icon will appear at the bottom right corner of the grid.
1272
+
1273
+ Next, each grid view helper should be placed in a partial of its own, requiring it from the master
1274
+ template for the usual flow. There must be no HTML or ERB code in this partial except for the grid helper.
1275
+
1276
+ By convention the name of such a partial follows the following pattern:
1277
+
1278
+ ```
1279
+ _GRID_NAME_grid.html.erb
1280
+ ```
1281
+
1282
+ In other words, a grid named `tasks` is expected to be found in a template called
1283
+ `_tasks_grid.html.erb` (remember that the default name of grids is '`grid`'.)
1284
+
1285
+ Next, method `export_grid_if_requested` should be added to the end of each action
1286
+ containing grids with enabled CSV export.
1287
+
1288
+ `export_grid_if_requested` intercepts CSV export requests and evaluates the partial with the required grid helper.
1289
+
1290
+ The naming convention for grid partials can be easily overridden by supplying a hash parameter
1291
+ to `export_grid_if_requested` where each key is the name of a grid, and the value is the name of
1292
+ the template (like it is specified for `render`, i.e. without '_' and extensions):
1293
+
1294
+ ```ruby
1295
+ export_grid_if_requested('g1' => 'tasks_grid', 'g2' => 'projects_grid')
1296
+ ```
1297
+
1298
+ If the request is not a CSV export request, `export_grid_if_requested` does nothing and returns
1299
+ `false`, if it is a CSV export request, the method returns `true`.
1300
+
1301
+
1302
+ If the action has no explicit `render` call, it's OK to just place `export_grid_if_requested`
1303
+ as the last line of the action:
1304
+
1305
+ ```ruby
1306
+ def index
1307
+
1308
+ @tasks_grid = initialize_grid(Task,
1309
+ name: 'g1',
1310
+ enable_export_to_csv: true,
1311
+ csv_file_name: 'tasks'
1312
+ )
1313
+
1314
+ @projects_grid = initialize_grid(Project,
1315
+ name: 'g2',
1316
+ enable_export_to_csv: true,
1317
+ csv_file_name: 'projects'
1318
+ )
1319
+
1320
+ export_grid_if_requested
1321
+ end
1322
+ ```
1323
+
1324
+ Otherwise, to avoid double rendering, use the return value of the method to conditionally call your `render` :
1325
+
1326
+ ```ruby
1327
+
1328
+ def index
1329
+
1330
+ ...........
1331
+
1332
+ export_grid_if_requested || render(action: 'my_template')
1333
+ end
1334
+ ```
1335
+
1336
+ It's also possible to supply a block which will be called if no CSV export is requested:
1337
+
1338
+ ```ruby
1339
+ def index
1340
+
1341
+ ...........
1342
+
1343
+ export_grid_if_requested do
1344
+ render(action: 'my_template')
1345
+ end
1346
+ end
1347
+ ```
1348
+
1349
+ If a column has to be excluded from the CSV export,
1350
+ set `column` parameter `in_csv` to `false`:
1351
+
1352
+ ```ruby
1353
+ g.column in_csv: false do |task|
1354
+ link_to('Edit', edit_task_path(task))
1355
+ end
1356
+ ```
1357
+
1358
+ If a column must appear both in HTML and CSV, but with different output, duplicate the column and use
1359
+ parameters `in_csv` and `in_html` to include one of them to html output only, the other to CSV only:
1360
+
1361
+ ```ruby
1362
+ # html version
1363
+ g.column name: 'Title', attribute: 'title', in_csv: false do |task|
1364
+ link_to('Edit', edit_task_path(task.title))
1365
+ end
1366
+ # plain text version
1367
+ g.column name: 'Title', in_html: false do |task|
1368
+ task.title
1369
+ end
1370
+ ```
1371
+
1372
+ The default field separator in generated CSV is a comma, but it's possible to override it:
1373
+
1374
+ ```ruby
1375
+ @products_grid = initialize_grid(Product,
1376
+ enable_export_to_csv: true,
1377
+ csv_field_separator: ';',
1378
+ csv_file_name: 'products'
1379
+ )
1380
+ ```
1381
+
1382
+ If you need an external CSV export button , add class `wg-external-csv-export-button`
1383
+ to any clickable element on page and set its attribute `data-grid-name` to the name of the grid:
1384
+
1385
+ ```html
1386
+ <button class="wg-external-csv-export-button" data-grid-name="grid">Export To CSV</button>
1387
+ ```
1388
+
1389
+ If you need to disable the default export icon in the grid, add `hide_csv_button: true` to the `grid` helper.
1390
+
1391
+
1392
+ ## Access to Records From Outside The Grid
1393
+
1394
+ There are two ways you can access the records outside the grid - using methods of the WiceGrid
1395
+ object and using callbacks.
1396
+
1397
+ ### Accessing Records Via The WiceGrid Object
1398
+
1399
+ Method `current_page_records` returns exactly the same list of objects displayed on page:
1400
+
1401
+ ```erb
1402
+ <%= grid(@tasks_grid) do |g|
1403
+ ...
1404
+ end -%>
1405
+
1406
+ <p>
1407
+ IDs of records on the current page:
1408
+ <%= @tasks_grid.current_page_records.map(&:id).to_sentence %>
1409
+ </p>
1410
+ ```
1411
+
1412
+ Method `all_pages_records` returns a list of objects browsable through all pages with the current filters:
1413
+
1414
+ ```erb
1415
+ <%= grid(@tasks_grid) do |g|
1416
+ ...
1417
+ end -%>
1418
+
1419
+ <p>
1420
+ IDs of all records:
1421
+ <%= @tasks_grid.all_pages_records.map(&:id).to_sentence %>
1422
+ </p>
1423
+ ```
1424
+
1425
+ Mind that this helper results in an additional SQL query.
1426
+
1427
+
1428
+ Because of the current implementation of WiceGrid these helpers work only after the declaration
1429
+ of the grid in the view.
1430
+ This is due to the lazy nature of WiceGrid - the actual call to the database is made during
1431
+ the execution of
1432
+ the `grid` helper, because to build the correct query columns declarations are required.
1433
+
1434
+ ### Accessing Records Via Callbacks
1435
+
1436
+ It is possible to set up callbacks which are executed from within the plugin just after the call to the database.
1437
+ The callbacks are called before rendering the grid cells, so the results of this processing can be used in the grid.
1438
+ There are 3 ways you can set up such callbacks:
1439
+
1440
+ Via a lambda object:
1441
+
1442
+ ```ruby
1443
+ def index
1444
+ @tasks_grid = initialize_grid(Task,
1445
+ with_paginated_resultset: ->(records){
1446
+ ...
1447
+ }
1448
+ )
1449
+ end
1450
+ ```
1451
+
1452
+ Via a symbol which is the name of a controller method:
1453
+
1454
+ ```ruby
1455
+ def index
1456
+ @tasks_grid = initialize_grid(Task,
1457
+ with_paginated_resultset: :process_selection
1458
+ )
1459
+ end
1460
+
1461
+ def process_selection(records)
1462
+ ...
1463
+ end
1464
+ ```
1465
+
1466
+ Via a separate block:
1467
+
1468
+ ```ruby
1469
+ def index
1470
+ @tasks_grid = initialize_grid(Task)
1471
+
1472
+ @tasks_grid.with_paginated_resultset do |records|
1473
+ ...
1474
+ end
1475
+ end
1476
+ ```
1477
+
1478
+ There are two callbacks:
1479
+
1480
+ * `:with_paginated_resultset` - used to process records of the current page
1481
+ * `:with_resultset` - used to process all records browsable through all pages with the current filters
1482
+
1483
+ While the `:with_paginated_resultset` callback just receives the list of records, `:with_resultset`
1484
+ receives an ActiveRelation object which can be used to obtain the list of all records:
1485
+
1486
+ ```ruby
1487
+ def index
1488
+ @tasks_grid = initialize_grid(Task)
1489
+
1490
+ @tasks_grid.with_resultset do |active_relation|
1491
+ all_records = active_relation.all
1492
+ ...
1493
+ end
1494
+ end
1495
+ ```
1496
+
1497
+ This lazy nature exists for performance reasons.
1498
+ Reading all records leads to an additional call, and there can be cases when processing all records should be triggered
1499
+ only under certain circumstances:
1500
+
1501
+ ```ruby
1502
+ def index
1503
+ @tasks_grid = initialize_grid(Task)
1504
+
1505
+ @tasks_grid.with_resultset do |active_relation|
1506
+ if params[:process_all_records]
1507
+ all_records = active_relation.all
1508
+ ...
1509
+ end
1510
+ end
1511
+ end
1512
+ ```
1513
+
1514
+ ## Bug reports
1515
+
1516
+ The author of the plugin welcomes any contribution.
1517
+ Please follow [these guidelines](https://github.com/leikind/wice_grid/wiki/How-to-submit-a-bug-report-or-a-question) when submitting a bug report.