Section 3.2. Ajax Links


3.2. Ajax Links

Now that we've got a decent canvas, we can add some Ajax to the mix. Change your index.html template to look like this:

<%= link_to_remote "Check Time",     :update => 'current_time',     :url    => { :action => 'get_time' } %> <div ></div>

We've turned link_to to link_to_remote and added a new option, :update. The value of :update refers to the HTML element ID where the Ajax response should be insertedin this case, a DIV. The generated HTML looks like this:

<a href="#"    onclick="new Ajax.Updater('current_time', '/chapter3/get_time',                {asynchronous:true, evalScripts:true});             return false;">Check Time</a> <div ></div>

Take a look at the generated HTML, and you'll see it uses Prototype's Ajax.Updater method. All the Rails Ajax helpers work this same way: they are Ruby methods, embedded in HTML templates, generating JavaScript, calling Prototype.

You may have noticed a red flag in the generated HTML link: HRef="#". While technically valid HTML, this kind of "link to nowhere" is generally a bad practice. If the user has JavaScript turned off, or a search engine is indexing the page, the link will be meaningless. Whenever possible, it's a good idea to provide a useful link, as a fallback for non-Ajax browsers. Chapter 6 covers the idea of degradability in more detail.


The essential mechanism of Ajax links is the onclick attribute, which is a way to hijack the behavior of a link. When an onclick is provided, the browser will evaluate it before following the link. The link will only be followed if the expression evaluates true (or if the user has JavaScript turned off). That's why the link_to_remote helper puts return false at the end of the onclick attribute.

3.2.1. Callbacks

So far, this is review. Let's dive deeper. The link_to_remote helper provides a set of callbacks so you can easily make things happen during the life cycle of an Ajax request by providing JavaScript snippets to be evaluated. For example:

<%= link_to_remote "Check Time",     :update => 'current_time',     :url    => { :action => 'get_time' },     :before => "$('current_time').update('Loading...')" %> <div ></div>

With that change, the current_time element is instantaneously updated with "Loading..." when the link is clicked, which helps the user see that things are working. There are callbacks available for every stage in the request life cycle. The most common are :before, :success, and :failure. You can provide multiple callbacks, to handle various response conditions. The most common uses are providing loading indicators and handling errors. For example:

<%= link_to_remote "Check Time",     :update   => 'current_time',     :url      => { :action => 'get_time' },     :before   => "$('indicator').show(  )",     :success  => "$('current_time').visualEffect('highlight')",     :failure  => "alert('There was an error. ')",     :complete => "$('indicator').hide(  )" %> <span  style="display: none;">Loading...</span> <div ></div>

In this example, the :before callback fires before the Ajax request starts, showing the "Loading..." element. If the request is a success (meaning it returns an HTTP status code in the 200 range), the :success callback creates a visual effect on the element. Otherwise, :failure fires, alerting the user to the problem. In either case (success or failure), the :complete callback takes care of hiding the "loading" element. The complete set of available callbacks is listed in Table 3-1.

This is the first time we've seen Prototype's hide( ) and show( ) methods, so it's a good opportunity to point out a common problem: for an element to be dynamically shown via JavaScript, its CSS display: none property must be defined inline, as opposed to in an external stylesheet. For example, this won't work:

<style type="text/css">   #indicator { display: none; } </style> <div >Hidden DIV</div> <script type="text/javascript">   $("indicator").show(  ); // won't work </script>

But this will work:

<div  style="display: none">Hidden DIV</div> <script type="text/javascript">   $("indicator").show(  ); // will work </script>

The same rule applies for any JavaScript method that will change an element's display propertysuch as Prototype's toggle( ) and script.aculo.us' visual effects. In general, it's wise to keep CSS rules external, but it's often necessary to make an exception for display: none.

Table 3-1. Ajax Helper callbacks and corresponding readyState properties
Helper callbackPrototype callbackreadyStateDescription
:before

  Request object has not yet been created.
:after

 0 (Uninitialized)Request object's open( ) method has not yet been called.
:loading

onLoading

1 (Loading)Request object's send( ) method has not yet been called.
:loaded

onLoaded

2 (Loaded)The request has been initiated.
:interactive

onInteractive

3 (Interactive)The response is being received.
:success

onSuccess

 The response is ready and its status is in the 200 range.
:failure

onFailure

 The response is ready and its status is not in the 200 range.
:complete

onComplete

4 (Complete)The response is ready.


3.2.2. Other Options

In addition to callbacks, link_to_remote has a few more options that can be used to customize its behavior. First, it supports the same options as link_tonamely :method and :confirm.

The :condition option is similar to :confirm: it allows you to conditionally execute the request, based on the result of some JavaScript expression. For example:

<li><%= check_box_tag 'checkbox' %> Thing #1</li> <%= link_to_remote "Delete checked items",     :condition => "$('checkbox').checked",     :url       => { :action => 'delete_items' } %>

When the link is clicked, the expression in :condition will be evaluated, and the request will only continue if it evaluates to true (in this case, if the checkbox is checked).

The :submit option is an interesting one: it allows you to simulate a form submission. By providing the ID of a page element, any fields contained in it will be sent along with the request. That means that you don't necessarily need a <form> element surrounding fieldsany element will do, such as a DIV or a tr. For example:

<div >   <input type="text" name="foo" value="bar" /> </div> <%= link_to_remote "Submit fake form",        :submit   => "fakeForm",        :url      => { :action => 'repeat' },        :complete => "alert(request.responseText)" %>

Clicking this link will scan the fakeForm DIV for any form fields, serialize the data, and send an HTTP POST to the repeater action, simulating a regular form submission, even though no <form> tag exists. This ability to simulate forms is especially useful when you remember that HTML doesn't allow nested forms. With the :submit option, you can easily work around that limitation.

Of course, the :submit option can also be useful within a form, when you need to submit it in more than one way. For example:

<form >   <input type="text" name="text_to_reverse"  />   <%= link_to_remote "Reverse field",        :url      => { :action => 'reverse' },        :submit   => "myForm",        :complete => "$('text_to_reverse').value=request.responseText" %>   <input type="submit" /> </form>

Here, we have a regular, non-Ajax form. But the "Reverse field" link uses Ajax to submit the form in the background and uses the response to change the value of the form field in place.

The :with option is used to construct a query string that's sent along with the requestbecoming the params object on the server side of the request. For example:

<%= link_to_remote "Link with params",        :url      => { :action => 'repeat' },        :complete => "alert(request.responseText)",        :with     => "'foo=bar'" %>

Notice that the value of :with has two sets of quote marks. That's because it's evaluated as a JavaScript expression, and in this case, we just want to provide a literal string expression. So here's what the helper would output:

<a href="#" onclick="   new Ajax.Request('/chapter3/repeat',     { parameters:'foo=bar',       onComplete:function(request){         alert(request.responseText)       }     }); return false;">Link with params</a>

But you can also include references to JavaScript variables or DOM elements. For example:

<input  type="text" value="bar" /> <%= link_to_remote "Link with dynamic params",        :url      => { :action => 'repeat' },        :complete => "alert(request.responseText)",        :with     => "'foo='+escape($F('myElement'))" %>

In this example, clicking the link will take the current value of the myElement field, escape it (so that it can safely be included in a URL query string), and send the value as a parameter named foo.

3.2.2.1. Linking to an arbitrary function

The link_to_remote helper we've been looking at is a specialized version of its big brother, link_to_function. It's used to generate a link that executes any JavaScript function. To see it in action, add this to index.rhtml:

<%= link_to_function "Toggle DIV", "$('indicator').toggle(  )" %></p>

The first argument is the link text, and the second is a JavaScript snippet that will be evaluated. This snippet uses the Prototype method toggle( ), which hides and shows elements on the page. In this case, it toggles the indicator DIV that we created earlier. The link_to_function helper renders as:

<a href="#" onclick="$('indicator').toggle(  ); return false;">Toggle DIV</a>




Ajax on Rails
Ajax on Rails
ISBN: 0596527446
EAN: 2147483647
Year: 2006
Pages: 103
Authors: Scott Raymond

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net