Adding AJAX to the Mix

Like many web acronyms, such as PHP and SOAP, the technology represented by the letters AJAX no longer match exactly the words being represented. In particular, AJAX need not be Asynchronous, nor involve XML. The technology has moved on, but the original acronym remains. So rather than spending time describing the processes in detail, let's describe what AJAX can bring to our application:

  • Direct client side access to the browser's rendering engine. That is, the page being presented to a user can change and be modified by the user's direct interaction with the browser.
  • Data transfer between the server and browser without page reload. Page elements can be updated without having to reload the whole page.
  • Client side processing. Much of the processing being carried out by AJAX elements is carried out by the user's browser, and therefore the processor within the user's computer rather than server processors. So AJAX can be used to spread the processor load.

Most of this processing is created through the use of JavaScript, a scripting language that has become the de facto universal browser scripting language. JavaScript has been around since 1995 and none of the "new" AJAX processes and methods are in themselves ground breaking. Even the key background data transfer API typically used by AJAX (that is XMLHttpRequest) had been used in many applications before the term AJAX was coined.

What AJAX has brought to web development is the packaging of these processes and methods into easy-to-use cross browser compliant libraries. These AJAX libraries have made it much easier to add JavaScript functionality into a web application. They provide structured access and syntax for functions and classes that have been tested and proven on the main web browsers such as Internet Explorer, Mozilla, Opera, Konqueror and Safari. This cross browser, functionality is important as each browser can treat JavaScript differently. For example, Microsoft's Internet Explorer uses a variation of JavaScript called JScript. AJAX libraries are coded to handle the variable behavior of the different browsers. This invaluable functionality makes it far easier to write one set of code that will work correctly in all these browsers.

Note

Do not confuse JavaScript with Java. They are two separate programming languages and should be treated quite separately. Code from one is not interchangeable with code from the other.

Rails uses two AJAX libraries:

These are located in the public/javascripts folder within a Rails application.

However, not only are these libraries made available within the default Rails installation, but also a number of Ruby Helpers have been created to simplify still further the incorporation of AJAX into a Rails application. It is possible to add many effects purely with Ruby objects and methods. However, some knowledge of JavaScript is helpful when tweaking an application to work exactly as wanted.

So without further ado, let us start using AJAX to provide an alternative search tool, and in doing so, start demonstrating what AJAX can do and how easily it can be added into our applications.

Make the AJAX Libraries Available to our Rails Application

Before our Rails application can use AJAX functions and classes, we must first make the libraries available to the pages generated by the application. In HTML we would do this by adding code, such as the following, to the head section of the page:

<script src="/javascripts/prototype.js type="text/javascript">

This would make the Prototype library available to the HTML page. We would have to add a second statement to make the script.aculo.us library available and additional lines for any other libraries.

In Rails, a helper method is used to make AJAX available to our application pages. The method is javascript_include_tag. The addition of an identifier'prototype' as shown in the following code generates the HTML code:

<%= javascript_include_tag 'prototype' %>

There is also a default action that will load all the AJAX libraries in the javascripts folder:

<%= javascript_include_tag :defaults %>

As well as loading the Prototype and script.aculo.us libraries, this statement will also load application.js. This file is empty (except for a comment) when created, and is the place to add any custom JavaScript classes and functions that we may wish to add to our application.

The ideal place to insert javascript_include_tag, is into layouts. Then all views that are presented within these layouts will get access to the AJAX libraries. For Rory, this means inserting a line between<head> and</head> in views/layouts/application.html.

Enhancing Search with Auto-complete

The auto-complete AJAX helpers create a system that monitors an input text box and provides the user with a drop-down list of results that match the user's input as they type. The user can then select from the list the item they are searching for. Note that for version 2.0 of Rails, auto-complete will not be included with the default Rails installation. However, it will still be available as a separate plug-in.

For Rory's application, we will create an auto-complete function for the company name search system. To do this he makes the following modifications:

  • In the Controller, companies_controller, he adds the following on its own line, before any of the method definitions:
auto_complete_for :company, :name
  • Then in the view companies/index.rhtml he replaces the text box in the search form with the following:
<%= text_field_with_auto_complete :company, :name %>
  • Now when Rory navigates to the company/index view in his browser the auto-completion function appears.
Enhancing Search with Auto-complete

In the figure above, the letter p was typed and a list of the three company names appeared in a drop-down list below the text box. On entering an l, the list reduced by one (the company with a name that did not contain "pl"). Once selected with a mouse click, the chosen complete company name is automatically entered into the text box.

There is one more modification for Rory to make to companies_controller. At the moment the search method is waiting for a "term" to be returned in the HTML header. However,

text_field_with_auto_complete :company, :name

...will return the data in a field entitled company[name]. If Rory were to look at the new URL for the search view, he can see that ?company%5Bname%5D= plus the search string is appended to the companies/search URL on submission of the form. %5B and %5D are hexadecimal representation of the HTML characters [ and] respectively. To access the data in the HTML header field company[name], Rails uses the following construct— params[:company][:name]. Therefore, the search controller method needs to be updated to use this construct instead of params[:term]. At the same time Rory can rearrange the start of the method so that the params statement is only required once. This will make it easier to modify this element as required. Rory also uses an if statement to catch instances where no name data is passed to the search method.

Rory's modified search method is shown below. The code first gathers the name passed from the auto_complete form, checks that data has been passed and is not an empty string, and then passes the name to a paginated company find method. The method uses the index view to display the resulting subset of companies that match the search criteria and a search specific page title is created by including the searched for string in @page_title.

def search
name = params[:company][:name]
if name and name.length > 0
@paginator, @companies = paginate :company,
:per_page => 10,
:order => 'name',
:conditions => ["UCASE(name) like UCASE(?)", prep_for_like(name)]
@page_title = "List of companies with names that contain '#{name}'"
render :action => 'index'
else
redirect_to :action => 'index'
end
end

So in three lines of code and a small modification to the search method, auto-complete has been implemented. It is so simple to implement that I think it is worth commenting on just how complex the underlying tasks are that Rory has just enabled.

  • An observer has been implemented that watches the text box and triggers an action when a user makes an entry of a single character into the text box.
  • The text entered into the text box is then retrieved.
  • An XMLHttpRequest is used to send the retrieved text to the server where it is used to generate a query of the database.
  • The results of that query are then returned to the browser via XMLHttpRequest.
  • The query results are used to build a drop-down list.
  • The drop-down list is displayed. That drop-down has the following features:
    • It is displayed within a box above the existing page content, immediately below the text box.
    • Styling is used to highlight the currently selected item.
    • Each entry is made active so that selecting it triggers the next event, and passes a reference to that next event from which the item selected can be determined.
  • The selected entry is then inserted into the text box.

In fact just the following two lines enable all of that functionality. The controller code:

auto_complete_for :company, :name

...and the view code:

<%= text_field_with_auto_complete :company, :name %>

Note

Cross referencing auto-complete

Most tutorials for Rail's auto-complete feature use the standard arrangement where the auto-complete entries are created from the field being queried. However, we do not have to generate the auto-complete drop-down list from the field we are ultimately populating or querying with the entry into the text box.

Consider this example. We have a table, listing the names of children and another table, listing the names of adults. We want to offer the users a search box for them to use to search through the surnames of adults, for surnames that match those in the table of children. The basic solution would be to raise the auto-complete on :adult and :surname. Then as the user started to enter text into the search box, they would be prompted with a list of existing adult surnames. However, the list would contain surnames that did not match names in the child surname list, but only appeared in the adult list.

An alternative approach would be to raise the auto-complete on :child and :surname, and then use the resulting params[:child][:surname] to build a query of the adults table. Both auto_complete_for :child, :surname and the handling of params[:child][:surname] would appear in the adult controller. That way, users will be prompted to select surnames that actually match the existing child surnames.

Note

Some of the most useful instances of auto-complete that I have implemented, have used this technique.

Auto-complete—Wow!, but...

My first reaction on implementing auto-complete was "Wow!". Most people who I demonstrated it to were similarly impressed. In the next few days I went through the application I was working on and added auto-complete to various text boxes; after all, it was only a few lines of code and did not take long to add.

Over the following weeks I became more familiar with auto-complete and as I did, I found myself slowly removing the auto-complete occurrences. I now use it sparingly in a few key instances. There were certain issues I had with using the Rails Auto-complete helper. They are as follows:

  • When errors occur, they are both obvious to the user and difficult to debug.When auto-complete fails, the usual result is that page styling gets altered when typing starts in the text input box. Typically the text size for the page decreases and the selection list fails to appear. (You can replicate this effect by commenting out the auto_complete_for statement in the controller).
  • Often error messages are generated neither at the browser nor at the server.
  • Errors occur after a user enters input at the browser. Most page source viewers pull a fresh copy of the page from the server. That is, a fresh copy showing the page before the user input.
  • Therefore they do not show how the page is altered as the user makes their input. This makes it difficult to determine the cause of the problem. A dedicated JavaScript debugging utility must be used to visualize the problem.
  • Auto-complete makes it difficult to alter if something goes wrong. There are no obvious configuration settings to tweak. We have no choice but to delve into the underlying helpers and methods.
  • Auto-complete can slow down user input. In a simple text box search form, if we want to search for entries containing the word "plumber", we simply: select the input box, type the word; and press the Enter key. If we do that with auto-complete enabled, we will not search for "plumber". With auto-complete, as we type "plumber" a list appears of matching entries in the database, with the first one selected in yellow. Entering carriage return via the keyboard selects the highlighted entry and inputs that into the form. It does not enter the string we have typed.

It is now clear to me that much of the reason I had problems with auto-complete was that I was adding it because it was a cool feature. I was not adding it because its features addressed a specific problem or requirement of the application. If not used appropriately, auto-complete simply adds complications and its own set of issues to the application.

Use of AJAX—the Lessons Learned from Auto-Complete

I have spent some time here describing and then picking apart a single Rails AJAX helper method. I did not do this because I think auto-complete is a particularly bad method. In fact, I believe that when used appropriately, auto-complete is a very useful addition to the developer's tool box. The reason I chose to highlight some of the problems with auto-complete is that it is easy to implement and test auto-complete for yourself. It is also easy to replicate the problems I have described above. Most importantly, a more general set of lessons can be derived from the lessons learned from using auto-complete.

  • The use of AJAX is seductive, in that it can be easy to implement, looks so good and currently is associated with a lot of kudos in web circles.
  • When we add an AJAX function, we will add both benefits and burdens. For example, AJAX is more difficult to debug than standard Rails applications.
  • If the "out-of-the-box" functionality does not quite match what we want, it can be difficult to make small changes to the functionality of an AJAX helper method.
  • AJAX can provide functionality that would be difficult to achieve in another way.

Note

Use AJAX because it addresses a problem that would be difficult to deal with another way.

Used appropriately, AJAX is an excellent addition to the functionality available to us as small business application developers. Use it where it is not needed and you will simply make your application more difficult to maintain.

Before I go on to describe some more AJAX techniques, there is one last point to make. If you return to Chapter 1: Introduction and look at the reasons put forward for using Ruby on Rails to build small business applications, you will find one of the benefits of this system was stated as:

... reliance on simple defined open standards means that the work of the user's web browser is kept to a minimum, with most of the complicated work being carried out at the web server.

AJAX reverses this approach. It adds processing and complication to the browser side of the application. It puts more reliance on users' browsers behaving as we expect them to. It increases the likelihood that a user's choice of browser and/or browser plug-ins could affect the functionality of our application. The more we use AJAX, the more we move away from the paradigm of concentrating the complication at the server where it is easiest to control. Using AJAX will always cause a trade off away from simple maintenance and toward a rich user interface. That should not stop us making the decision to accept such a trade off when appropriate, but should mean that we make it as a conscious decision following an assessment of the pros and cons.

Having highlighted some of the issues with using AJAX, I will now describe a couple of AJAX functions that I have found particularly useful.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.250.11