All tables below are displayed with the table helper. I'm regularly working on this page, so if you come back and get an error, don't panic, it's just that I'm enhancing the example. Comments and suggestions are welcome at rb at raphinou dot com or on the #rubyonrails IRC channel.
The example data array we use is defined as
simple_array = [ [ 1, "Ruby", "http://www.ruby-lang.org"],
[ 2, "Python", "http://www.python.org"],
[ 3, "Java", "http://www.java.sun.com"],
[ 4, "Perl", "http://www.perl.org"],
[ 5, "lua", "http://www.lua.org"]
]
#controller
@e1 = { "array" => simple_array }
#view
<%= table(@e1, "display" => :default) %>
This generates this table:
| 1 | Ruby | http://www.ruby-lang.org |
| 2 | Python | http://www.python.org |
| 3 | Java | http://www.java.sun.com |
| 4 | Perl | http://www.perl.org |
| 5 | lua | http://www.lua.org |
with this HTML code
#controller
fields = TableFields.new(:table_id => "example2")
@e2 = { "array" => simple_array , "fields" => fields}
#view
<%= table(@e2, "display" => :default) %%>
with of course the same visual result:
| 1 | Ruby | http://www.ruby-lang.org |
| 2 | Python | http://www.python.org |
| 3 | Java | http://www.java.sun.com |
| 4 | Perl | http://www.perl.org |
| 5 | lua | http://www.lua.org |
Building on the previous examples, we will no add column headers.
#controller
#We reuse the TableFields instance, but set a new id
fields3 =fields.dup
fields3.table_id = "example3"
#The table fields have to be in an array
field_names = [ "id", "name", "homepage"]
#and we pass it to the TableFields instance
fields3.field_names = field_names
@e3 = { "array" => simple_array, "fields" => fields3}
#view
<%= table(@e3, "display" => :default) %%>
| id | name | homepage |
|---|---|---|
| 1 | Ruby | http://www.ruby-lang.org |
| 2 | Python | http://www.python.org |
| 3 | Java | http://www.java.sun.com |
| 4 | Perl | http://www.perl.org |
| 5 | lua | http://www.lua.org |
We would have done this if we passed the field_names to the constructor of TableFields. The accessor has the same name as the symbol used in the constructor.
#controller
#The table fields have to be in an array
field_names = [ "id", "name", "homepage"]
#and we pass it to the TableFields constructor
fields_ex4 = TableFields.new(:table_id = "example3", :field_names => field_names)
@e4 = { "array" => simple_array, "fields" => fields_ex4}
#view
<%= table(@e4, "display" => :default) %%>
| id | name | homepage |
|---|---|---|
| 1 | Ruby | http://www.ruby-lang.org |
| 2 | Python | http://www.python.org |
| 3 | Java | http://www.java.sun.com |
| 4 | Perl | http://www.perl.org |
| 5 | lua | http://www.lua.org |
Don't you think the id coumn should not be displayed? You can list the names of the columns to not be displayed in the hidden_fields Array:
#controller
#We reuse the TableFields instance, but set a new id
fields5 = fields_ex4.dup
fields5.table_id = "example5"
#The table fields have to be in an array
hidden_fields = [ "id"]
#and we pass it to the TableFields instance
fields5.hidden_fields = hidden_fields
@e5 = { "array" => simple_array, "fields" => fields5}
#view
<%= table(@e5, "display" => :default) %%>
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
You may wonder why include the column in the data array if it is not going to be displayed. The answer is simple: you can still use the values in this column to build links for example.
We can use a LinkSpec instance to define a link, and this link will be generated in the fields we define with the TableFields instance. Let's start with a static link, i.e. a link to a fixed destination: http://www.rubyonrails.org. The definition of the link is done like this:
#define the link
ror_link_spec = LinkSpec.new
ror_link_spec.href = "http://www.rubyonrails.org"
#and add it to the table definition
fields_ex6 = TableFields.new(:table_id => "example6", :field_names => field_names)
fields_ex6.add_link("name", ror_link_spec)
fields.hidden_fields = ["id"]
@e6 = { "array" => simple_array, "fields" => fields_ex6}
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
Now, the previous example isn't that useful... Wouldn't it be great to link to the homepage of the language? That's possible! Here's the result:
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
To achieve that result, the table helper calls the method field_value which has 2 parameters: the first one is the name of the column from which to take the value; and the second is the line on which the table helper is working at the time of the call. The internal name of the line on which the helper is working is line. Here's the code needed to generate the table above:
#define the link
homepage_link_spec = LinkSpec.new
#set the href value to call the field_value method,
#the second paramter being the current line
homepage_link_spec.href = %q(field_value("homepage", line))
#and set href to be eval'd in the context of the line the helper is working on
homepage_link_spec.eval_href=true
#The rest is like the previous examples:
fields_ex7 = TableFields.new(:table_id => "example7", :field_names => field_names)
fields_ex7.add_link("name", homepage_link_spec)
fields_ex7.hidden_fields = ["id"]
@e7 = { "array" => simple_array, "fields" => fields_ex7}
The table helper being a Ruby on Rail component, it also makes it easy to generate links to Runy on Rails controllers. I defined another controller, language_details which displays details of the language whose id is passed in the URL. We can easily generate a link to that controller, passing the id of the language. Here's the code:
#define the links
#homepage
homepage_link_spec = LinkSpec.new
homepage_link_spec.href = %q(field_value("homepage", line))
homepage_link_spec.eval_href=true
#link to the details controller
details_link_spec = LinkSpec.new
#set rails controller and action we link to
details_link_spec.controller = "language_details"
details_link_spec.action = "display"
#params are parameters passed in the url, available in @params in the controller
details_link_spec.params = { "id" => %q(field_value("id", line))}
#Say that the parameter id has to be evaled in the context of the current line in the table helper
details_link_spec.set_param_eval("id")
#rails_parameters are any rails parameters passed to url_for
details_link_spec.rails_parameters = { :controller_prefix => "rails"}
#we need to pass a reference to the current controller to have url_for usable in the helper
fields_ex8 = TableFields.new(:table_id => "example8", :field_names => field_names, :controller => self)
#add the two link specs, each for one field.
fields_ex8.add_link("homepage", homepage_link_spec)
fields_ex8.add_link("name",details_link_spec)
#Hide the id field
fields_ex8.hidden_fields = ["id"]
@e8 = { "array" => simple_array, "fields" => fields_ex8}
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
It is possible to sort the data displayed. To that end, the header of the column by which value we want to order the display becomes a link. By clicking that link you order the data displayed by the values in that column. Here's an example to sort our data by the name column. I set 2 tables side by side, to show you can reorder one table without loosing the order of the other tables (example9 is left, example10 is right).
This is achieved with this code (we reuse the links defined above):
#we need to pass a reference to the current controller to have url_for usable in the helper
#we tell the helper that the column "name" should be a criteria for sorting
#the headers_rails_parameters is necessary in this case because I run this demo as a servlet
#behind an apache proxy forwarding all request to /rails to the servlet.
#In normal operations this should not be necessary. It shows however how you can pass
# any url_for parameters
fields_ex9 = TableFields.new(:table_id => "example9", :field_names => field_names, \
:controller => self, :headers_links => ["name"], :headers_rails_parameters =>{:controller_prefix => "rails" } )
#by default, all params are put in the generated links. This is useful to keep the sort orders of
# other tables on the same page eg.
#In our case, it is not necessary to pass it over to the next controller.
details_link_spec.set_rails_parameter(:overwrite_params, {"sort_example9" => nil} )
fields_ex9.add_link("homepage", homepage_link_spec)
fields_ex9.add_link("name",details_link_spec)
fields_ex9.hidden_fields = ["id"]
#we tell the helper to manage the sorting. If we want to manage it ourselves, for example in
#the SQL queries we issue, we can
# leave this to false (which is the default value)
fields_ex9.manage_sorting = true
@e9 = { "array" => simple_array, "fields" => fields_ex9}
fields_ex10=fields_ex9.dup
fields_ex10.table_id="example10"
fields_ex10.headers_links = ["name","homepage"]
@e10 = { "array" => simple_array, "fields" => fields_ex10}
It is possible to set header, row and field CSS classes, possibly based on the data values displayed. Let's start with the headers. I want the color header to be yellow for the name column and green for the homepage column. I first define the corresponding classes in my stylesheet:
.yellow {
background-color : yellow;
}
.lightgreen {
background-color : lightgreen;
}
header11_builder = lambda { |fields,field_name|
css_class=""
if field_name =="name"
css_class = "yellow"
elsif field_name =="homepage"
css_class = "lightgreen"
end
css_class
}
#and pass it to the TableFields instance
fields_ex11.header_class_builder = header11_builder
Here's the result we get
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
We can use that technique to give a special css class to the header of the column we use to sort. In this example I give the yellow class to the header of the column used to sort data (in real life it would rather be sort_active or something like that I guess):
fields_ex12=fields_ex11.dup
fields_ex12.table_id="example12"
header12_builder = lambda { |fields,field_name|
css_class=""
if @params["sort_#{fields.table_id}"] and @params["sort_#{fields.table_id}"]== field_name
css_class = "yellow"
end
css_class
}
fields_ex12.header_class_builder = header12_builder
@e12 = { "array" => simple_array, "fields" => fields_ex12}
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
In the same way, we can assign css class to rows. I want even rows to have a white background, odd rows a yellow background. HEre's the code
row13_builder = lambda { |fields,line, line_number|
css_class=""
if line_number.remainder(2)!=0
css_class = "yellow"
end
css_class
}
fields_ex13=fields_ex12.dup
fields_ex13.table_id="example13"
fields_ex13.row_class_builder = row13_builder
@e13 = { "array" => simple_array, "fields" => fields_ex13}
and here's the result (row numbers start at 0):
| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
You can in the same way set row ids. The reason I implemented it is that I had a long table displayed and wanted to go back to that row when I had triggered an action on it. Here's how you do it:
rowid14_builder = lambda { |fields,line|
"#{fields.table_id}_#{line[0]}"
}
fields_ex14=fields_ex12.dup
fields_ex14.table_id="example14"
fields_ex14.row_id_builder = rowid14_builder
@e14 = { "array" => simple_array, "fields" => fields_ex14}
And here's the result. I added the links manually.
row with id table14_1--row with id table14_3--row with id table14_5| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |
| Java | http://www.java.sun.com |
| Perl | http://www.perl.org |
| lua | http://www.lua.org |
You can also modify the value of the fields before they get displayed. This can be used to turn a value of 1 or 0 in true or false, or for translation in different languages. In this example I will display all language names in uppercase. First the code:
fields_manipulator15 = lambda { |value|
return value.upcase
}
fields_ex15=fields_ex12.dup
fields_ex15.table_id="example15"
fields_ex15.manipulated_fields = [ { "field_name" => "name", "manipulation" => fields_manipulator15 }]
@e15 = { "array" => simple_array, "fields" => fields_ex15}
then the result:
| name | homepage |
|---|---|
| RUBY | http://www.ruby-lang.org |
| PYTHON | http://www.python.org |
| JAVA | http://www.java.sun.com |
| PERL | http://www.perl.org |
| LUA | http://www.lua.org |
If you have long tables to display, it's often convenient to display them on separate pages with navigation links (just like Google returns their search results). It's very easy to achieve that result with the table helper. The only implementation for now requires you to pass the whole array to display, and the helper manages itself the display. That means you cannot, for the moment, limit a SQL query to just resturn the rows you want to display (like Mysql and Postgresl accept the LIMIT # OFFSET # directives in a query). That's a limitation that will be lifted as soon as the need shows up, which should be really soon actually.
But let's go on with the example. I configure the table to only display 2 rows per page. That way, 3 navigation links will be displayed. Each link is in a span of class table_navigation_link for links to other pages than the one currently displayed, and of class table_active_navigation_link for the (inactive) link of the current page. Here's the code:
fields_ex16=fields_ex12.dup
fields_ex16.table_id="example16"
#activate the display in several pages
fields_ex16.manage_page_display=true
#set the number of rows paer page
fields_ex16.rows_per_page=2
@e16 = { "array" => simple_array, "fields" => fields_ex16}
and here's the result. Note that you can still order the rows, and that you stay on the page you were on even if you change the sort order. You can also change the number of rows per page with parameters passed in the URL
3 rows per page| name | homepage |
|---|---|
| Ruby | http://www.ruby-lang.org |
| Python | http://www.python.org |