#43
Jun 11

AJAX with RJS

This episode will walk you through adding AJAX functionality to a form using RJS. See how to easily update multiple elements on a page.
Tags: ajax
Download (37.3 MB, 11:34)
alternative download for iPod & Apple TV (18.8 MB, 11:34)
<!-- products/show.rhtml -->
<% form_remote_for :review, :url => reviews_path, :html => { :id => 'review_form' } do |f| %>
# reviews_controller.rb
def create
  @review = Review.create!(params[:review])
  flash[:notice] = "Thank you for reviewing this product"
  respond_to do |format|
    format.html { redirect_to product_path(@review.product_id) }
    format.js
  end
end

# create.rjs
page.insert_html :bottom, :reviews, :partial => 'review', :object => @review
page.replace_html :reviews_count, pluralize(@review.product.reviews.size, 'Review')
page[:review_form].reset
page.replace_html :notice, flash[:notice]
flash.discard

40 comments:

InMan Jun 11, 2007 at 01:58

Nice. Week ago I messed with this. Got many goods tips.


weskycn Jun 11, 2007 at 02:37

so good


chineseGuy Jun 11, 2007 at 03:08

thanks!~


vigosan Jun 11, 2007 at 04:03

One of the best screencast.


Michael Jun 11, 2007 at 04:05

Hello, Is possible that flash message disappears after 5 seconds?


Cristiano Betta Jun 11, 2007 at 06:07

Hi, very cool screencast. I was looking for some good ajax advice and now I have it! I have one question though: where did you place the .rjs file?


quantum Jun 11, 2007 at 06:37

put the .rjs somewhere in the views directory. typically the directory where the rest of the views for a specific controller can be found.


Fred Jun 11, 2007 at 07:10

Hey Ryan,
I noticed that you use "form_remote_for". But, the Agile book and the Ajax on Rails book all refer to "remote_form_for". Are they different? Or, is "form_remote_for" an Edge Rails thing?

Great screeencast as always!


Fred Jun 11, 2007 at 07:14

Michael,

Yes, it's probably possible to do what you are describing. You'd use some of the Scriptaculous visual effects, which Ryan did not cover in this episode. You'd do something like "page[:notice].visual_effect :fade, :duration => 5"
I did not try this. So, I can't verify. But, in theory, you'd do something like that to get the effect you want.


Ryan Bates Jun 11, 2007 at 07:18

@Fred, if you look in the API docs you can see form_remote_for is an alias of remote_form_for - so either one will work. While I like the sound of remote_form_for better, I get confused because there's no corrosponding remote_form_tag. There's only form_remote_tag and that parallels better with form_remote_for. So I've got used to using that.


Fred Jun 11, 2007 at 07:57

ryan,
Ah, that explains it. I never ever look at api docs. haha. . .


maze Jun 11, 2007 at 08:36

another useful screencast! Is there a way to combine the update of the flash via AJAX with your screencast 18 (looping through flash)?


Jake Jun 11, 2007 at 08:42

Great episode!


Ryan Bates Jun 11, 2007 at 09:29

@maze, yep. You can loop through the flash hash as is done in that episode. For example, place this in the rjs file:

flash.each do |key, msg|
page.replace_html key, msg
end

You may want to move this into a helper method so it can be called from any rjs file.


Tobias Jun 11, 2007 at 11:40

You´re the king, Ryan!


maze Jun 11, 2007 at 11:50

Hey Ryan! thanks a lot! So I'll try to build my first helper method! :-)


Jaffet Jun 11, 2007 at 13:24

This is great, waiting with the validation. When the next part is coming?
Thanks.


Ted Jun 11, 2007 at 18:42

Fantastic! They just keep getting better.


Ryan Jun 12, 2007 at 21:39

Thank you!


Andreas Jun 12, 2007 at 23:44

Ryan,

Just a small tip, instead of setting the flash and then unsetting it you can use this:

flash.now[:notice] = "..."

This way it won't be passed to the next action :)


Ryan Bates Jun 13, 2007 at 07:42

@Andreas, thanks for the tip. The reason I can't do that in this case is I need the flash to stick around if it's still a normal HTML request (non-AJAX). I don't want to set it in two places so in this case I think discarding it in the RJS is the best solution.


Andreas Jun 13, 2007 at 13:56

@Ryan, hadn't thought of that, very well then :).


Rebort Jun 14, 2007 at 14:47

What happens when two different people submit reviews at the same time (or nearly the same time)? Would they be able to see each other's messages?


Ryan Bates Jun 14, 2007 at 15:32

@Rebort, nope, not without reloading the page. However, you can use a technology called Comet to "push" data to the client. This way all users viewing that page will automatically update when a review is added. I haven't done it myself, and I'm not sure how hard it is to do in Rails.

For more information on Comet:
http://en.wikipedia.org/wiki/Comet_(programming)


Rebort Jun 14, 2007 at 17:34

Excellent -- thanks for the clarification and link, Ryan!


Oskar Jun 15, 2007 at 15:01

This might have been the perfect screen cast. You really nailed it, Ryan!


Mikoangelo Jun 21, 2007 at 15:06

I'm having some problems getting it to work. I have the controllers Tips and Programs, which share a HABTM relationship.

In the `Tips#new` view I have a <ul> list of items in Programs, in the style of "HABTM Checkboxes", the railscast.

On the bottom of the page (outside the main form) I have a `form_remote_for :programs, :url => programs_path`. In the `Programs#create` function I create a Program instance, and respond_to format.js. I have a create.js file in the same folder, which renders a partial also used in the `Tips#new` view (btw, can I get DRY on this? It seems I have to have a duplicate file in both view/* folders).

However, whenever I submit the remote form, it shows the JavaScript code directly in the browser, as if it send the wrong Content-Type header. I have tried going over the code several times, but I cannot find the error.


Ryan Bates Jun 21, 2007 at 16:42

@Mikoangelo, I need to see the code in order to determine what's wrong. But here's not the best place to post that. Instead I recommend making a thread over at railsforum.com. I visit that frequently and would be glad to help over there.


bryce Jun 25, 2007 at 15:19

Great screen cast Ryan. When you update the html with the @review object, are you able to access it's relational objects? In other words, in the partial that you're rendering, can you access the user table to display: @review.user.human_name ?

Thanks for all the screen casts. They're great.

Bryce


Ryan Bates Jun 25, 2007 at 16:51

@bryce, yep. You can access associations through the model in the partial. It's just like any other view.


Gabriel Bogéa Jun 26, 2007 at 06:38

Hi Ryan, thank you for the work you've been doing. Lot's of great tips.
On this subject I'd like to see how to handle validation erros, and which would be the best way to display them.


Aurels Aug 25, 2007 at 16:20

So nice! Thanks so much for all your casts.

I'm interested in the way to do the same but with validations (RJS and "normal" version). In "normal" version, how to cleanly re-call/re-render the 'show' action of 'ProductsController' with the non-created-and-with-validation-errors Review, as we are in two different controllers? A link to help me? Thanks again!


Vlad Aug 31, 2007 at 02:44

How about automagically calling flash.discard in every format.js block?


Ryan Sep 29, 2007 at 23:43

Ryan, Very nice work, been helping me a great deal with setting up a blog. but I run into a huge hurdle where the rjs is not rendering after the respond_to call. I've checked the rjs with a link_to_remote and its working fine. code done just like in your notes. Any reason that this would happen


Hutch Oct 23, 2007 at 23:40

Excellent screencast. I've got it working beautifully. Thing is... ordinarily, I'm zebra striping my comments (comments instead of reviews, in my case) with <div class="comment <%= cycle("odd", "even") %>"> in my comment partial. Since the page.insert_html is a single insert, and thus, it never cycles, I'm not getting my zebra striping until I refresh.

How might I go about rewriting my zebra striping code to account for this page insert? And, as a followup, if I wanted to display which number comment it was, something I previously did with each_with_index, how might I get that info as well?

Thanks so much!


Freddy Nov 23, 2007 at 14:06

Hi Ryan,

I got problem with rjs.

It's redirection problem ... I think.

It starts at http://localhost:3000/freddys.

And after I hit update button which send all params via form_remote_tag to server and use rjs to update current page.

However, it render at http://localhost:3000/user/update/2 and display content of the rjs file.

Any idea ... thanks


Roob Dec 19, 2007 at 01:18

Ryan,

this is another great screen cast. My problem is, that i want to achieve exactly what you show here, including model based validation. Any possibility to achieve that "the rails way"?


sri Feb 05, 2008 at 05:52

i was trying to download the file and store this video in another PC,
but i was unable to download it,is it not possible with out quicktime player installed in my PC ?.
aah,but later i installed player but this time the movie itself is playing in the browser..
plz help me how to download the file seperately
thanks,,


Romulo Velasquez Apr 24, 2008 at 20:49

Hi,

Probably heard it a hundred times already but thanks for all the screencasts. They really are excellent and find them to be a great resource!

I encountered a strange problem that I'm not really sure I understand. While experimenting with the ideas from your screencast I decided to add a respond_to block to a method being called through AJAX. After I did that whenever I made the AJAX call Rails complained that it couldn't find the partial that I was trying to render in my corresponding RJS file. I check my RJS and surely it was render :partial => 'form' and the partial was named _form.html.erb. The only way I could get it to work was if I removed the format.js statement from the respond_to block.

Basically this works (controller):

def fill
@post = Post.new
@post.title = "Fill"
@post.body = "Fill"

respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
end
end

But this doesn't:

def fill
@post = Post.new
@post.title = "Fill"
@post.body = "Fill"

respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
format.js #This line causes an issue
end
end

What gives?

Rom


kino May 10, 2008 at 08:04

This was new for me. It's amazing at how well thought out rails is.

Thanks again for the video Ryan.

Add your comment:

(required)

(not displayed)

(SKIP THIS ONE)


(required)

subscribe:
sponsored by:
if you want to help:
required:
Get Quicktime Player