Skip to content

HTML Page Changes using AJAX with Paloma and jQuery in Rails 4:

Recon Vancouver edited this page Jul 23, 2014 · 6 revisions

HTML Page Changes using AJAX with Paloma and jQuery in Rails 4:

This simple tutorial for Rails 4 was based on this post, which helped me figure out how to get jQuery ajax working with Rails render :html responses in my controllers. It explains how to use Paloma to handle jQuery UJS AJAX requests, responding using HTML fragments generated on the server.

Installation:

First, get the latest version of the Paloma gem from rubygems.org and add it to your gemfile:

gem 'paloma', '~> X.X.X'

...and run bundle update.

Then add //= require paloma to your app/applications/javascripts/applications.js manifest. Somehow these asset pipeline things always go better if you rake asset:precompile and restart your server every now and then...

Add a Rails link_to Tag to your Views:

Now we will add an ajax response to a Rails Instrument#show controller action, together with views and javascripts. Start by putting this in your Instrument#show view:

#/app/views/instruments/show.html.haml

.instrument_name
= link_to "Click to see the instrument name...", @instrument, :remote => true, "data-type" => :html, class: "click_show_instrument_name"

Adding remote => true will tell Rails UJS (unobtrusive javascript) to use jQuery's .ajax() method to handle the link submission. This tutorial is for Rails 4, but whatever versions of Rails you are using will need jQuery UJS for this to work.

If you take a look at Rails' HTML output, it should look like this:

<a class="click_show_instrument_name" data-remote="true" data-type="html" href="/instruments/8">Click here to see the instrument name</a>

Adding "data-type" => :html to the link_to tag tells Rails to add the data-type="html" attribute to this HTML. The data-remote="true" HTML attribute, which is added thanks to the :remote => true in the link_to tag, tells Rails that this is an AJAX request. The two together are telling the Rails controller to respond using AJAX when the link is clicked, and to return HTML rendered on the server side.

Paloma will not run when a Rails action controller renders JSON or javascript (using render :json or render :js), which is why you need to have "data-type" => :html in your link_to tag for this to work.

Setup the Rails Controller to Respond With HTML Using Paloma:

In your Instrument#show action, you can use something like this to respond to the AJAX request which will be received when the link_to link is clicked:

# app/controllers/instruments_controller.rb

def show
  @instrument = Instrument.find(params[:id])
  
# this will make the controller @instrument.instrument_name variable available in Paloma
  js :instrument_name => @instrument.instrument_name
  
  respond_to do |format|
    format.html { render :layout => ! request.xhr? }
  end
end

Thanks to format.html { render :layout => ! request.xhr? }, your controller will respond by rendering your default layout as a conventional HTML get request, unless the request type is .xhr, in which case it will respond with an HTML fragment using AJAX from Rails' jQuery UJS.

Setup the Paloma Controller:

Make a Paloma folder in your asset pipeline javascripts folder and put the following javascript in there:

// app/assets/javascripts/paloma/instruments.js

var InstrumentsController = Paloma.controller('Instruments');

InstrumentsController.prototype.show = function(){

  $(document).on('ready, page:change', show_instrument_name(this.params['instrument_name']));
  
};

This code will be automatically run by Paloma whenever an .xhr request is received by Instrument#show. It instantiates a Paloma controller (var InstrumentsController = Paloma.controller('Instruments');), and declares a function on that controller object which will run whenever jQuery detects a page:change.

Since Rails 4 uses Turbolinks to update the page with fragments without a full page reload, it is important to use $(document).on('ready, page:change') rather that $(document).ready to invoke functions in jQuery. Otherwise they will only be called when the page is fully reloaded in the browser (which might not be very often using Turbolinks).

To promote code reuse, the show_instrument_name() function that actually makes changes on the Instrument#show pages is in a regular javascript file in the Rails asset pipeline. Our Rails controller @instrument.instrument_name variable is passed to this javascript in our Paloma controller by using this.params['instrument_name']) as an argument.

Change the Page using jQuery:

Now we are ready to actually make change the page on a successful jQuery AJAX response:

// app/assets/javascripts/instrument_name.js

// here the Active Record (AR) controller variable is passed as an argument:
show_instrument_name = function(AR_instrument_name) {
  
  // jQuery is used to select the page elements for transformation:
  // this is the link_to link that makes our ajax request:
  click_show_instrument_name = $('.click_show_instrument_name[data-type=html]');
  // and this is where we will show the controller @instrument_name variable
  instrument_name = $('.instrument_name');
  
  // we hide the div that will show the instrument_name
  $(instrument_name).hide();

  // and then when the link_to tag receives an AJAX success response:
  $(click_show_instrument_name)
    .on('ajax:success', function(event, data, status, xhr) {
      
      // it is hidden,
      $(click_show_instrument_name).hide();
      
      // and our instrument_div is used to output the value from our Rails controller
      $(instrument_name).show().html(AR_instrument_name);
  });
}

Click the Link to Show the Instrument Name:

With all this working, if you click the .click_show_instrument_name link ("Click here to see the instrument name"), you should see something like this in your server logs:

[INFO] Started GET "/instruments/9" for 127.0.0.1 at 2014-07-22 13:29:21 -0700
[INFO] Processing by InstrumentsController#show as HTML
[INFO]   Parameters: {"id"=>"9"}
[DEBUG]   User Load (0.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 16 ORDER BY "users"."id" ASC LIMIT 1
[DEBUG]   Instrument Load (0.3ms)  SELECT "instruments".* FROM "instruments" WHERE "instruments"."id" = $1 LIMIT 1  [["id", "9"]]
[INFO]   Rendered instruments/show.html.haml (0.9ms)
[INFO]   Rendered .../paloma/_hook.html.erb (0.4ms)
[INFO] Completed 200 OK in 10ms (Views: 2.4ms | ActiveRecord: 1.0ms | Solr: 0.0ms)

And the .click_show_instrument_name div should disappear and be replaced by the .instrument_name div, with the contents of your @instrument_name controller variable inside.

And in only 10 milliseconds! It sometimes takes as much as 30-50 milliseconds from a remote server...