Introduction and warnings
In this tutorial, we will try to use Ajax with Rails in order to display a table of items with several functionalities :
- pagination (split the table on several pages)
- sorting (table ordering by one of its column)
- searching (selecting items to be displayed with a query)
A live demonstration of this application is available at :
http://dev.nozav.org/ajaxtable/
This is a very common task in web application development. The interest to use Ajax for this is to provide a dynamic interface which doesn’t need to reload the entire page when the table changes. The interest of using Rails is… well, if you are reading this you should already know, but Ajax is really nicely integrated with Rails, and using it is very easier with this great framework. Nevertheless, the code in this tutorial should work the traditional way (by reloading the entire page) if javascript is not available on client side. This is very important for accessibility.
The code which follows is highly inspired from several Rails wiki pages, in particular How to make a real-time search box with the Ajax helpers, and How to paginate with Ajax. I just put things together and sometimes slightly adapted the code.
Now, the warnings. I am not a Rails guru, and far from being an Ajax expert. I am a beginner in both areas. So take this document as a beginner introduction to beginners : code could be cleaner, explanations more accurate, and some things could probably have been designed more efficiently an elegantly. Moreover, as english is not my native language, you will find many faults and inaccuracies through this text. Please excuse me in advance.
Application installation and configuration
The first thing we have to do is to install and setup the application. If you already had done this before, you could probably skip this section.
Requirements
In this tutorial you are supposed to have a recent Rails version installed (at least version 1.0) and a functional database system. Here we will use sqlite, but you can replace it by the one you prefer without any problem.
Files
First, we have to create the “skeleton” of our application in the directory of our choice1 :
$ rails ajaxtable$ cd ajaxtable
As it is a very basic application, we will use only one model, which will represent table items, and one controller for these items. We will generate the corresponding files with the Rails scripts :
$ ruby script/generate model Item$ ruby script/generate controller Item
Database
Once we have the files, we have to setup a data source. For simplicity here we will use Mysql in conjunction with Rails schema. This means that we will describe our database inside Rails and let it manage database creation.
First, we have to update the development data source in config/database.yml. Enter ur database password in password :
development: adapter: mysql database: ajaxtable_development username: root password:
Next, we will create the database by using Rails migration tools.
$ ruby script/generate migration database_creation
This should have created a db/migrate/001_database_creation.rb file in which we will define our database schema the following way :
This just defines one table named items with three columns2 : a string field called name, and two integer columns called quantity and price (nothing really original, yes…).
Then, we just have to do a :
$ rake db:migrate
U can also use the following command but it shows some negotiable depreciation warnings.
$ rake migrate
And you should now have a development.db file in your db directory which is your sqlite database created by Rails with the items table inside.
Creating the model
As you probably know, a Rails application is divided into three main types of components : models, views and controllers. We will create them one by one.
The model of our application will be very simple here. In fact, as we don’t have any complex query to send to the database, it will stay the same as generated by Rails in our application installation, ie empty. So we will not touch the app/models/item.rb file.
Well, I hope this step has not been too difficult !
Creating the view
The view of our application will be split into two components : a layout, a view and a partial.
Layout
The layout is a page template which will be used to render several views. It contains some non-varying elements such as HTML header and footer, menus, design elements, etc. The utility of a layout is very limited in our example, as we will have only one page. But it is a tutorial, after all…
Here, the layout will be located in app/views/layouts/item.rhtml, and will contain something like :
An important thing here is the javascript_include_tag tag which will be replaced by the javascript libraries used by Rails to provide Ajax features.
Also note the @content_for_layout instruction, which will be replaced by the generated content when needed.
View
The view component will be used to render a particular action of our controller, which we will describe immediately after this. As it is a list action of our item controller, the view will be located in app/views/item/list.rhtml.
The content of the file will be the following :
There is nothing really complicated in the beginning. A stupid introduction text and a search form which will be used to filter items on their names.
Then, we have an image element whose id is spinner and which is hidden by default. This image will be shown briefly during Ajax operations on the page, and then hidden again when everything is finished. You can get some public domain images here :
You have to put the chosen image file in the public/images directory of your application.
The following observe_field instruction is a bit more unusual. What this instruction does here is to add a new Ajax observer on the query field. This observer will periodically (here, every 2 seconds, look at the frequency parameter) check the content of this field and will react if this content has changed.
The action to be taken is described by the remaining parameters :
- update gives the id of the page element which will be updated. Here it is the
tablewhich encloses our partial call just a bit further in the code. - url gives the action that will render the new HTML content to be inserted. Here the
listaction of our controller will handle every request. - with indicates the way the field content will be passed to the
urlaction. Here we will add aqueryparameter to our Ajax request with value equals to the content of the observed field. - before indicates an action to be performed during the time the Ajax request is treated.
- success indicates an action to be performed when the Ajax request has been succesfully treated.
The concrete effect of all this stuff is quite simple. When the user type something in the query field, the observer will detect the new content of the field, and then generate an Ajax request sent to the server with the url and with parameters. As soon as the request is sent, the before action toggles the visibility of the spinner XHTML element, and the user can see the image. When the request answer his received, the table XHTML element is updated, and the success action hides the spinner image again.
Just for information, here is what the observe_field function returns with the given parameters :
//<![CDATA[new Form.Element.Observer('query', 2, function(element, value) {Element.show('spinner'); new Ajax.Updater('table', '/item/list', {asynchronous:true, evalScripts:true, onSuccess:function(request){Element.hide('spinner')}, parameters:'query=' + value})})//]]>
We spent some time here to detail the different options because we will meet them again in nearly all the other Ajax methods in this tutorial.
And, to finish this view description, we have a call to a partial element called items_list. We will describe this concept and its content in detail after setting up our controller.
Creating the controller
The controller will handle different kinds of requests in order to update the view by calling the model depending on the request type and its parameters.
Our Item controller will be very simple here, and will only include one action called list. We will not implement any other CRUD (Create, read, update, delete) action in this tutorial.
So here is the content of app/controllers/item_controller.rb :
Let’s explain this a bit.
Our controller only defines one action, called list. This action will have to handle every request sent to our application.
First, we define a variable called items_per_page which will represent the number of lines our table will show on each page.
Next, we define another variable called sort, depending on the request parameter of the same name. The case can be used to mask the real fields name in our database, which can be more suitable for security reasons. The reverse string in the sort parameter indicates that sorting should be made in descending order.
Then, a conditions variable is constructed if a query request parameter is present. It is an SQL-like instruction which will be used to filter our database query results based on the content of the name field.
After that, we assign the total number of items in our database matching the conditions to the @total variable.
And eventually, we find a call to the Rails paginate instruction. We give to this instruction the model it should be associated to (:items), a sort field, conditions to be applied to the query, and the number of items we want on each page. And it automagically returns us a paginator object called @items_pages and the items for the current page number (this page number is passed as a page request parameter, which is transparent here). The paginator object will be used later to display pagination links.
All we have seen for the controller until now applied to every request passed to the application, whatever its type. The most used kinds of HTTP requests are the traditional GET and POST, but Rails and Ajax can make an important use of a third type, whose name is XmlHttpRequest4.
This kind of request is launched by javascript, which will send it in the background via HTTP to the server. One use of the kind of request is to get a fragment of XHTML page used to update a part of the browser display, without reloading the entire page. This can give the user the impression of a fastest and more responsive interface.
That is what the final part of our controller deals with : it tests if the request it has received is of type xml_http_request5. In this case, it will not render the entire list view, but only a fragment of it, the now famous partial whose name is items_list.
So, as we can see, this xml_http_request test is the only “really Ajax” thing we met in the controller. That is because Ajax things are linked to the interface of our application, ie the view, and more specifically the part of it which will be managed with Ajax, ie the partial.
So, could you guess what we are going to do now ?
Creating the partial
A partial view component is used to render only a part of a page. It is very useful to separate some repeated view constructions in order to reuse them and to follow the Don’t Repeat Yourself (DRY) Rails principle. But it is also really useful with Ajax.
Indeed, the effect of our Ajax actions in this tutorial will always be to redisplay the table of items, whereas it is to change the page, the order or the search. So we will have to refresh the table, but not the entire page, otherwise an Ajax call is of very limited utility. That’s why we will separate all the elements which will have to be updated in a partial file6.
Partial filenames always begin with an underscore, so here our partial will be in app/views/item/_items_list.rhtml.
Its content will be the following :
Some explanations may be needed here. The partial contains the table pagination and sorting management. We will see both in a more detailed way.
Pagination helpers
In the beginning, we have a test to see if the total number of items found is greater than zero. In this case, we display this total number and then a paragraph which will be empty if there is only one page in our items pagination.
If we have more than one page of results, we clearly have to display pagination links. Rails already have methods to help dealing with them, but we will have to customize them a little. For this, we will create a helper.
A helper is a Ruby function which is used to help generating the view. The aim is to separate the code in these functions from the view itself, and to make this code reusable.
Our helpers will all be located in app/helpers/item_helper.rb. Each method in this file will be accessible from our view. If we wanted them to be usable by every view of our application, we could have put them in application_helper.rb.
Well, enough blabla, here is the code of our pagination_links_remote helper :
This method takes a paginator object as argument. This is a Rails object which keeps information about the current pagination state (number of pages, current page, etc.).
We then define a page_options hash which contains only one item called window_size. This is a parameter which tells Rails how many pages it has to show around the current one. For example, if window_size equals one, we will have something like :
1 … 5 6 7 … 13
If window_size equals 2 :
1 … 4 5 6 7 8 … 13
We could then make a call to the pagination_links function, which would generate the correct XHTML code for our links. But the problem is that it will generate “classic” XHTML links, but not the Ajax one. So we will have to redefine the links ourselves. This is done with the pagination_links_each method.
This method iterates over the pages to be displayed and then applies the block passed as its argument. Our block first defines two kinds of options :
- options are defined for the Ajax link generation. These are very similar to those defined in the
observe_fieldelement. The only new thing is the call to@params.merge, which will add the current request parameters to the link by replacing an existing page param by the page number in the block. - html_options are just defined to generate the “classic” XHTML link, in order to let pagination work if javascript is not available.
Then, there is just a call to the link_to_remote function, which will generate the complete XHTML for our link, including the javascript and the classic XHTML href part.
Sorting helpers
Back to our partial. After the pagination links we start to define the table by itself. The definition of the table header is a bit complicated, because that is where we define the links to sort our data by a column or another. Each header cell makes use of two helpers.
The first helper is nothing but necessary. It is called sort_td_class_helper, and his only goal is to add a class="sortup" if the column is currently the one used to sort the table, or a class="sortdown" if it is used to sort in reverse order. The only utility is to allow, with CSS, to indicates to the user which column is currently used as a sorting field.
The code is nothing interesting which is already shown in the figure pagination_links_remote helper
Then, we have a second helper, called sort_link_helper which is also in that.
This helper is in fact very similar to the pagination_links_remote one, seen above. It takes two arguments :
- text, which is just the text to be displayed as the column header and sort link
- param, which is the name of the request parameter associated with the column.
The first two lines define a new variable, called key, which gets the value of the param argument, ie the sort key. The string _reverse is concatenated to it if param is already the sorting key. This is used to implement sorting in both ascending and descending order. If the user select a sort link, it will sort by ascending order ; if it selects the same link again, it will sort by descending order, and so on. You can look at the controller if you don’t find these explanations clear enough.
The rest of the helper is to define the options for the final call to the link_to_remote function. They are very similar to those of pagination_links_remote :
- the options hash, used for the javascript Ajax link, contains the
urlto be called to generate the new HTML, the id of the element to update, and twobeforeandsuccessactions to show/hide the spinner image. - the html_options hash, used for the HTML link. It generates the content of the
hrefattribute with theurl_forRails function.
Table body
The end of the partial just displayed our table body, with one item on each line. The only thing which may be noted here is the use of the cycle Rails function, which will automatically and alternatively add a “odd” or “even” style class to our table rows, which can be very useful to display it in a more visible way.
That’s all folks !
We have now seen every pieces of our application more or less in detail. In theory you could see the result by starting the WebRick server and point your browser to :