The Robot Factory – Part Two

Asynchronously Adding and Deleting Resources in Sinatra

In part one of this tutorial we went through how to add and delete robots into a database. In this tutorial I’m going to show how you can leverage the XMLHttpRequest to perform actions in the background without the need for a page refresh. That’s right – we’re going to turbo-charge the app with some Ajax! Unless you’ve been hiding in a cave for the last five years, you have probably heard of Ajax – it is basically a method of posting and retrieving data in the background and is typically used to update parts of a web page without doing a full page refresh.

The fact that the code from part one of the Robot Factory works before Ajax is added is a good thing, since it means it will work for any browsers that don’t have JavaScript enabled or don’t support XMLHttpRequest (the kids today call this progressive enhancement). Adding Ajax is basically a 3 step process:

  1. Stop the actual request from happening, so the page does not reload
  2. Insert the html for the new resource on to the page, possibly with some sort of whizzy effect
  3. Save the new resource to the database in the background

I’m going to be using the fantastic RightJS JavaScript library to help take care of the Ajax.

To get started, we need to include the necessary JavaScript files into our layout:

  script src="http://cdn.rightjs.org/right.js"
  script src="/application.js"

Next we need a route, specifying that the JavaScript will be written in the same file:

get('/application.js') { content_type 'text/javascript' ; render :str, :javascript, :layout => false }

And last of all, we need a place to put the custom JavaScript that we will be adding. This goes underneath all the other views at the bottom of the page:

@@javascript
// javascript goes here

Deleting with Ajax

We do this by adding some JavaScript. We can write this as another inline view underneath all the rest of the view code The actual JavaScript needed isn’t very much, but it does rely on some RightJS magic. We’ll start by making the delete request a bit snappier:

@@javascript

"form.destroy".onSubmit(function(event) {

   this.parent().fade();

   event.stop();

   this.send();

 });

This bit of JavaScript uses the awesome UJS syntax that RightJS uses to find all elements matching the css rule `form.destroy` (ie all forms with the class of ‘destroy’ – the delete buttons on each robot). It then dynamically adds the following events to it:

  1. this.parent().fade(); The parent node (ie the robot itself) will fade away and disappear using the nice fade effect that is built in to RightJS.
  2. event.stop(); The actual action of the form will be stopped, so it won’t get sent in the normal way
  3. this.send(); This will send the form using Ajax.

One nice advantage of RightJS is that this function applies to all forms with the class of destroy – even ones that are created on the fly, after the JavaScript file has loaded, like we will be doing in the next section. Before we do that, we have to consider what happens when Sinatra receives the delete request asynchronously. At the moment our handler looks like this:

delete '/delete/robot/:id' do

 Robot.get(params[:id]).destroy

 redirect to('/')

end

This is fine, except we don’t actually want to refresh the page by doing a redirect if we’re using Ajax. There’s a great method in Sinatra (well, actually it’s from Rack) called request.xhr? which will return true if the request is an Ajax request and false otherwise. We can use this to stop the redirect when Ajax is used:

delete '/delete/robot/:id' do

 Robot.get(params[:id]).destroy

 redirect to('/') unless request.xhr?

end

Adding With Ajax

Now let’s have a look at building a robot using Ajax. The following JavaScript goes underneath the previous code block:

"form.build".onSubmit(function(event) {

 event.stop();

 this.send({

   onSuccess: function(xhr) {

   $('robots').insert(xhr.responseText);

  }

 });  

});

This works in a similar way to the delete function above. It applies this to all forms with a class of ‘build’ (ie the big Build A Robot button). The first thing it does is stop the form from being sent, then sends it using Ajax. This function also contains a callback that happens when the Ajax request has been made successfully. It says to insert the response text into the list with an id of ‘robots’. Basically this will add a new robot on to the end of the list. For this to work, we need to make sure that a robot is returned. At the moment the handler looks like this:

post '/build/robot' do
 robot=Robot.create
 redirect to('/')
end

This simply creates a new robot. We need to do things a bit differently if there the robot is created using Ajax, so we once again reach for the request.xhr? method:

post '/build/robot' do

 robot=Robot.create

 if request.xhr?

   slim :robot, { :layout=> false, :locals => { :robot => robot } }

 else

   redirect to('/')

 end

end

After the robot has been created and saved to the database, we check if the request is an Ajax request. If it is then the result of the robot view that we created before (told you it would be useful). We include a few extra options to make sure that we don’t include the layout in this snippet (:layout=>false) and that the new robots parameters are included (:locals => { :robot => robot }). If it isn’t an Ajax request then we just do the same as before – redirect back to the home page and the new robot will be drawn anyway as that request is processed.

Spinning Around

The only thing that is left to do is add one of those cool Ajax spinners that show something is actually going on in the background. Head over to http://www.ajaxload.info/ and generate a gif of your favourite spinner design. Save it in ‘public’ folder in the same directory as the Sinatra code then make the following alterations to your code. In the views, add the image to the index page (make sure you give it a memorable id, I’m using ‘waiting’):

@@index

…

img#waiting src="https://s3.amazonaws.com/daz4126/waiting.gif"

Add some css, so that the spinner is not displayed by default:

@@styles

…

#waiting{display:none;position:absolute;left:200px;top:100px;}

Then in the JavaScript, tell RightJS the id of the spinner:

@@javascript

Xhr.Options.spinner = 'waiting';

...

And that’s it! We’ve made the interface much snappier using the Ajax calls and avoided having to reload the page every time we add and delete a robot. This also means that the database doesn’t get hit as often to retrieve the @robots array. The page also has a much better feel to it and the disappearing effect adds some extra polish to the app. Of course you could easily do this in one of the many other JavaScript frameworks out there, but I feel that RightJS offers a very succinct and straightforward way of dealing with Ajax. I’d love to hear any thoughts about this as well as your experience using of using Ajax in web apps in the comments.

You can see the full code on github and have a play around with my version.

Enjoy building the robots!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • rsludge

    Nice tutorial, thanks!
    I never heard about RightJS before, but it seems to be very powerful framework, I’ll try to use it in my projects.

    • http://ididitmyway.heroku.com/ Darren Jones

      Thanks rsludge! RightJS is brilliant, I’m sure you will find it useful in your projects. Nikolay Nemshilov who writes it is a Javascript Jedi who is really helpful and responsive to requests.

      DAZ