« November 2007 | Main | Januar 2008 »

Dezember 2007 Archives

20.12.07

AJAX calls with fallback

When you have a list of - say - articles (that’s what I have here) and you want to change the order the items appear on the fly(maybe the newest ones first, or alphabetic or maybe clockwise…you name it…), then you have two possibilities:
The old way by providing a link that sends a “normal” request to the server which returns the same site again, with the difference of an alternated order of articles.
In rails this is pretty basic stuff:

<%= link_to 'Oldest ones first', :action => "index",
:sort_string => "created_at ASC" %>

The index-action in the controller then makes nothing different than before besides doing a database-query with the altered order-statement. And returns the site. Here the excerpt from the controller:

def index
if params[:sort_string]
    sort_string = params[:sort_string]
else
    sort_string = "created_at DESC" # the default sort order
end

@articles = Article.find(:all, :order => sort_string)
...

And there is the new cool, web 2.0 way: performing an AJAX call to the controller and just update the section of the page that needs to be updated. Without having to refresh the whole site. There you go:

<%= link_to_remote 'Oldest ones first', :url => { :action => "index",
:sort_string => "created_at ASC" }, :method => :get %>

This doesn’t work without two more lines of code. But that’s done with the blink of an eye.
In the index action of the controller you need to respond to the javascript format by adding this:

... # excerpt
respond_to do |format|
    format.html # index.html.erb
    format.js # index.js.rjs THIS LINE IS ADDED
    format.xml  { render :xml => @articles }
end
... # excerpt

Further you need to create a file with name index.js.rjs* in /app/views/YOURCONTROLLER/. Add the following line of code to this file:

page.replace_html :list, :partial => "list"

…where :list is the id of your div-element that contains the list of articles.
:partial => “list” calls the _list.html.erb partial (which contains that specific div).

The list must be wrapped in a div-element and the whole thing goes into _list.html.erb!!!
So just copy the code of the list you already have to the partial file and wrap everything in a div with id “list”. Then call the partial from that point where you removed it with <%= render :partial => “list” %>.

And then you’re done. When you hit the link, the list refreshes without loading the whole site again.

But wouldn’t it be nice to make this feature always work, even for browsers that have JavaScript disabled? I think so, ‘cause it guarantees that this works without relying on an enabled JavaScript setting. And that’s super-easy. Just modify the link in the view this way:

<%= link_to_remote 'Oldest ones first', {:url => { :action => "index",
:sort_string => "created_at ASC" }, :method => :get }, :href =>
url_for(:action => "index", :sort_string => "created_at ASC") %>

The only thing I had to add was two braces and the stuff following :href =>.
If JavaScript is enabled, the AJAX call will be send and otherwise it works as a “normal” call. Nice, heh?!?

But as you can see, the parameters passed are duplicates. The :url and :href hashes are quite similar. To DRY** things up, just write a small helper-method in application_helper.rb, that does this duplication for you:

def link_to_remote_with_fallback(name, options = {}, html_options = {})
    html_options[:href] = url_for(options[:url])
    link_to_remote(name, options, html_options)
end

And BOOM! The code’s DRY again:

<%= link_to_remote_with_fallback 'Oldest ones first', :url =>
{ :action => "index", :sort_string => "created_at ASC" } ,
:method => :get %>

Credits: DRYing up linktoremote for degradable URLs

*index is the name of the action in the controller. So modify this to match your action.
**DRY - Don’t repeat yourself

15.12.07

Doing AJAX-links right in Rails 2.0

Ok, maybe this is a trivial topic, but as this was my first “real” AJAX experience in Rails 2.0, I spent some time solving this issue:

I wanted to sort a list of articles differently using AJAX. So in the appropriate action of the controller I added the format.js line:

respond_to do |format|
    format.html # index.html.erb
    format.js
    format.xml  { render :xml => @articles }
end

And then I created a new view template in the view folder belonging to that controller: index.js.erb.
This was my first fault! The correct file extension is .js.rjs! First this didn’t make sense to me, but I got some explanation here: http://www.railsforum.com/viewtopic.php?pid=47149

And then - in my index.js.rjs template - I wrote the following line:

page.replace_html :list, :partial => "list"

This replaces the HTML of the div list with a rendering of the partial _list.html.erb. I never had any doubt that this line was correct. ;-)
So far so good, but I got really ugly results clicking a link_to_remote link:

<%= link_to_remote 'Oldest Articles first', :update => 'list',
:url => { :action => "index", :sort_string => "created_at ASC" }, :method => :get %>

The result was something like:

try { Element.update ...

…followed by more strange ruby code, mixed with some of my list data in between, replacing the list-div. Here I expected the newly rendered, resorted list.

The problem was the :update => ‘list’ statement. That way, the code was not interpreted as JavaScript and got directly inserted into the div-tag. And as you can see, I specified to div to be updated in the page.replace_html call in index.js.rjs.

So, the correct call of link_to_remote would be without the :update parameter:

<%= link_to_remote 'Oldest Articles first', :url => { :action => "index",
:sort_string => "created_at ASC" }, :method => :get %>

Problem solved!

In short:

  1. the right file extension for responding to JavaScript is .js.rjs
  2. when using link_to_remote with page.replace_html don’t use the :update parameter

As I said above, this is not such a big problem when done correctly, but sometimes it’s an accumulation of 2 or 3 little issues that turn into a big one. At least for me.

08.12.07

Rails 2.0 released

"This is a fantastic release that’s absolutely stuffed with great new features, loads of fixes, and an incredible amount of polish."

Check out more in DDHs Post:

Riding Rails: Rails 2.0: It's done!

About

DanielHi. I'm Daniel Pietzsch and this is my innoQ-Blog. I'm a 26y old student at FH Bochum and working student at innoQ.
In this blog I mainly write about the progress concerning my diploma thesis which will be an in-house application for innoQ based on Ruby on Rails, but some other (geek) stuff might appear here, too.

daniel [dot] pietzsch [alt-L] innoq [dot] com

I recommend

Categories

Recent Comments

License

Creative Commons License This weblog is licensed under a Creative Commons License.
Powered by
Movable Type 3.31