Emergent Web Technologies Spring 2009 Class 9

esse quam videri
Jump to: navigation, search

Back to EWT Spring 2009

Introduction

We already talked about getting remote data with JavaScript using JSONp and jQuery, but, until now, we haven't been able to use Ajax. Now that we can create simple sites with Ruby on Rails I'll cover how to write some simple Ajax code to make your pages more dynamic.

Activity 1

With this activity we'll take a regular scaffolded Rails site and add a little bit of Ajax. The idea is that the site is designed to work without Ajax, but if the user has JavaScript enabled, we can make it a little bit more dynamic.

1. Open Instant Rails and the Ruby console window


2. Generate a new Rails site

 rails ajax

3. Navigate to the new rails site and create the scaffold for a simple locations database

 cd ajax
 ruby script/generate scaffold Location name:string address:string city:string state:string zip:string
rake db:migrate


4. Make sure that your site works by starting the server

ruby script/server

And then open the link to your site http://localhost:3000/locations


5. Create a new location using your site and then go back to the site index.

Notice that you can click around on the urls to view the location, edit it and delete it. We can use Ajax to enhance the site so that you won't need to reload the page every time you want to move around the site.

Note: this is meant to demonstrate Ajax more than it is to demonstrate good user interface design. In real life, some of the Ajax used here might not be the best way of designing things.


6. The first thing we can do is modify the page so that we don't need to reload to view a location. We can just view it on the same page as the locations index.

Open up the location index view at

 /app/views/locations/index.html.erb


7. Add a new div to the bottom of the page where we can show the preview content

 <div id="preview-div">
 </div>

And modify the show link to add a new class that will we target with JavaScript

 <%= link_to 'Show', location, :class => 'show-link' %>


8. We're going to be using jQuery for Ajax calls, so you'll need to download it here

I usually modify the name of the file so it's just called jquery.js. You can modify the file name if you want.


9. Once you have the file downloaded you'll need to put it in the following directory

 /public/javascripts

The public folder is where files that aren't part of your Rails application can be created and accessed.


10. Go back to your view page and add the following line to the top of the page

 <%= javascript_include_tag "jquery" %>

The javascript_include_tag helper function will automatically include the jquery.js file from the javascripts folder (convention over configuration).

Reload your page and look at the source to make sure that your JavaScript is being included.


11. Now we can finally create the JavaScript to add some Ajax to our page. Create a new file for the JavaScript at

 /public/javascripts/show.js

And add the following code to it

var Show =
{
   init: function()
   {
       jQuery('.show-link').click(function(event)
       {
           event.preventDefault();
           Show.load(this.href);
       });
   },
   
   load: function(url)
   {
       jQuery.ajax(
       {
           url: url,
           success: function(html)
           {
               jQuery('#preview-div').html(html);
           }
       });
   }
};

jQuery(Show.init);

The Show object contains a couple utility functions for adding Ajax to our page.

The init function finds any links that have have a class name "show-link" and adds a click handler to them. The click handler prevents the link from being followed and instead calls the Show.load function and passes the url of the link to it.

The load function uses jQuery's ajax function to make an Ajax call to the link's url. If the call succeeds, an anonymous function is called that fills the preview div with the content that came back from the Ajax call.


12. To get this script to load on your index page, you'll need to include it. So modify the javascript_include_tag call so it's loading jQuery and the show library

 <%= javascript_include_tag "jquery", "show" %>


13. Reload your index page in the browser and click on the show link. Instead of taking you to another page, the show link should load in the content for the location in the preview div.

If you open up the Firebug console you should be able to see the Ajax calls as they go by.

Activity 2

Let's add some more changes to the locations page we created by adding an Ajax powered delete button.

For the delete button on your index page, Rails already adds some JavaScript to delete the entry. So you don't have to worry about the item getting deleted with a GET request, because only the JavaScript function will delete it. But the JavaScript is added inline and we don't have any control over what it looks like. We'll create our own JavaScript for deleting an entry.

1. Modify the "Destroy" link by removing the extra Rails parameters and adding a class name that we can target

 <%= link_to 'Destroy', location, :class => "delete-button" %>


2. Create a JavaScript file located at

 /public/javascripts/delete.js

And add the following code to it

var Delete =
{
   init: function()
   {
       jQuery('.delete-button').click(function(event)
       {
           event.preventDefault();
           
           var link = this;
           Delete.location(link);
       });
   },
   
   location: function(url)
   {
       jQuery.ajax(
       {
           url: url,
           type: "DELETE"
       });
   }
};

jQuery(Delete.init);

This script is similar to the show script. It's an object called Delete that contains some utility functions for deleting a location.

The Delete.init function adds a click handler to any links that have a class "delete-button" and prevents the links from being followed when clicked. Then it passes the link that was clicked on to the Delete.location function.

The Delete.location function makes an Ajax call to the url that was clicked, but this time uses DELETE instead of GET. Rails has already set up a controller action called destroy automatically gets called when you send a DELETE request to an item.


3. Include the JavaScript by modifying the javascript_include_tag again

 <%= javascript_include_tag "jquery", "show", "delete" %>


4. Reload your page and click the Destroy click. If everything goes well, it should look like nothing happened. But if you check the Firebug console, you should see a DELETE request that was made to link for the location.

If you reload the index page, the location should be gone.


5. Everything technically works at this point, but it's not very friendly for the user. Let's add a callback function that will remove the deleted location from the page once it has been successfully deleted.

You'll need to modify the JavaScript in your delete.js file. Change it to look like this

 var Delete =
 {
   init: function()
   {
       jQuery('.delete-button').click(function(event)
       {
           event.preventDefault();
           
           var link = this;
           Delete.location(link, function()
           {
               jQuery(link).closest('tr').fadeOut();
           });
       });
   },
   
   location: function(url, callback)
   {
       jQuery.ajax(
       {
           url: url,
           type: "DELETE",
           success: callback
       });
   }
 };
 
 jQuery(Delete.init);

This Delete.location function now takes in a callback function. We pass that callback function on to the Ajax call. If the Ajax call was successful, the callback function will be called automatically.

If you look at the call to the Delete.location function from the Delete.init function you can see that we're passing in an anonymous function that will be used as the callback. The anonymous function uses jQuery's closest function to find the closest table row to the link that was clicked and then fades it out.


6. Add a new location and go back to the index page. Click the destroy button again. This time, you should see the item you click on fade away after it has been deleted.

Activity 3

So far, we've used Ajax to get the raw HTML from one page and display it on another page. But we can also use Ajax to get structured data like XML or JSON and use it to make changes on our page.

In this activity we'll create a simple message queue that displays the latest messages that have been added to the queue.

1. Scaffold a section of the site that will allow users to add and manage messages on the site.

 ruby script/generate scaffold Message text:text posted_by:string
 rake db:migrate

Now you should be able to start adding messages to the database if you go to http://localhost:3000/messages.

2. It would be nice if we had a page that displayed messages live, as they came in. Let's create a new controller to do that.

 ruby script/generate controller live

You should now have a live controller at

 /app/controllers/live_controller.rb

3. Create an empty index action in the live controller.

 def index
   
 end

And then add a view for the live controller's index by creating an empty file at

 /views/live/index.html.erb

If you start your server up again and open http://localhost:3000/live you should see an empty page. This is where we will add our JavaScript to show the live messages coming in.

4. Create a new JavaScript file at

 /public/javascripts/live.js

And add the following JavaScript to it

 var Live =
 {
   init: function()
   {
       setInterval(function()
       {
           jQuery.ajax(
           {
               type: 'GET',
               url: '/messages',
               dataType: 'xml',
               success: Live.loadMessages
           });
       }, 1000);
   },
   
   loadMessages: function(xml)
   {
       jQuery('#messages').empty();
       jQuery(xml).find('message').each(function()
       {
           var poster = jQuery('<dt>' +
                                   jQuery(this).find('posted-by').text() + 
                                '</dt>');
                               
           jQuery('#messages').append(poster);
           
           var message = jQuery('<dd>' + jQuery(this).find('text').text() + '</dd>');
           jQuery('#messages').append(message);
       });
   },
 };
 
 jQuery(Live.init);

The Live.init function sets up an interval that will call a polling function every second. Again, we're using jQuery's ajax call to get the data. This time we're making a call to the messages controller's index page. And using the Live.loadMessages function to handle a successful response. Notice also that we're passing along a datatype we'd like to receive back. jQuery will add a header to the Ajax call that indicates it wants XML back and Rails knows that it should respond with XML.

The Live.loadMessages function takes in the response from the Ajax call (which jQuery will automatically convert to XML because we specified "xml" as a datatype). We then search the XML for the posted-by and text properties from the messages that were returned and use that information to fill out a definition list on the page with an ID of "messages".

5. We need to include the /views/live/index.html.erb by including the new live.js file and the jQuery file

 <%= javascript_include_tag 'jQuery', 'live' %>

Also, add a header and the definition list that will be filled with the new messages

 <h1>Newest Messages</h1>
 <dl id="messages">
 </dl>

6. Now, you can load http://localhost:3000/live in one tab and start adding messages under http://localhost:3000/messages and see them show up automatically on the live page