#43 AJAX with RJS
Jun 11, 2007 | 11 minutes |
Ajax
This episode will walk you through adding AJAX functionality to a form using RJS. See how to easily update multiple elements on a page.
- Download:
- mp4Full Size H.264 Video (20.5 MB)
- m4vSmaller H.264 Video (13.4 MB)
- webmFull Size VP8 Video (34.1 MB)
- ogvFull Size Theora Video (27.6 MB)
Nice. Week ago I messed with this. Got many goods tips.
One of the best screencast.
Hello, Is possible that flash message disappears after 5 seconds?
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?
put the .rjs somewhere in the views directory. typically the directory where the rest of the views for a specific controller can be found.
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!
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.
@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.
ryan,
Ah, that explains it. I never ever look at api docs. haha. . .
another useful screencast! Is there a way to combine the update of the flash via AJAX with your screencast 18 (looping through flash)?
Great episode!
@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.
Hey Ryan! thanks a lot! So I'll try to build my first helper method! :-)
This is great, waiting with the validation. When the next part is coming?
Thanks.
Fantastic! They just keep getting better.
Thank you!
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 :)
@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.
@Ryan, hadn't thought of that, very well then :).
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?
@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)
Excellent -- thanks for the clarification and link, Ryan!
This might have been the perfect screen cast. You really nailed it, Ryan!
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.
@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.
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
@bryce, yep. You can access associations through the model in the partial. It's just like any other view.
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.
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!
How about automagically calling flash.discard in every format.js block?
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
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!
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
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"?
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,,
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
Ryan,
I know that you're not Rails tech support, but wondering how much this has changed with the release of Rails 2.1.
I have a clean 2.1 demo site - using cookie store - with basically the same set up as your screencast. The HTML POST form works fine, but when I switch to form_remote_for in my view file, I get an 'ActionController::InvalidAuthenticityToken' error. This only occurs when using remote. I do have a responds_to for js, and a corresponding create.js.erb, but it appears that the action never gets called. The request dies before it gets there due to the authenticity error.
If I comment out protect_from_forgery in application.rb, I receive a 200 response, but it appears that the request has a content length of 0.
I know I'm missing something simple. I'll keep digging, but wondering if you've you run across this in your travels?
Ok, I figured it out, but not sure why it is...yet. Turns out my example _was_ slightly different; I was using a table - instead of divs - to format the form.
As soon as I removed the table, it worked.
I'll have to dig into the source code in order to figure it out.
Thanks for listening. ;-)
this is beautiful. i think i'm going to cry :) thanks for all your help!
how can download the file via rjs
For those you want validation:
Put a errors div above the form_remote_for function in your view.
<div id="errors"></div>
Then in your reviews controller, handle errors for the object.
respond_to do |format|
if(@review.save)
format.html { redirect_to(@review.product) }
format.js
else
format.js { render :update do |page|
page.replace_html :question_errors, error_messages_for(:review)
page.visual_effect :highlight, :errors, :duration => 2
end
}
end
end
You will want to update your create.rjs file to clear the errors message on a valid save.
page.replace_html :question_errors, ""
I hope I haven't missed anything.
Ryan - if you did not want to use create.js, and wanted to call another file instead, how would you do it? The screencast alluded to the possibility, but I haven't figured out how. Thanks!
Hey all.
Hoping for help with this.
All is working fine except for this line:
page.replace_html :notice, flash[:notice]
My flash messages are conditionally shown, as Ryan demonstrated in episode #18. Therefore, the RJS is crapping out because the "notice" element isn't available.
Simple problem, but looking to hear how you handled this.
Thanks.
chris, try this:
page << "if ($('flash_notice')) {"
page[:flash_notice].show
page.replace_html :flash_notice, flash[:notice]
page << "} else {"
page.insert_html :before, :new_comment_form, content_tag(:div, flash[:notice], :id => "flash_notice")
page << "}"
page.delay(1) do
page[:flash_notice].hide
end
flash.discard
This is one of the best episodes I have seen so far! great tips!
Definitely one of the best Railscasts ever.
A
Excellent tutorial but I got this problem I have followed all the steps of your tutorial but it still refreshes the page. . . Can u please help me... I'm stuck with this.
mersi
Thx from newbee`s. :)
Great cast!!! and thank you very much Andrew Cetinick for your comment on validation, works perfectly!
Can some one help me with the f.hidden_field :product_id, :value => @product.id
comment controller
def create
@Comments = Comment.create!(params[:comment])
flash[:notice] = "Just Add !!"
redirect_to product_path(@comments.product_id)
end
error:
NoMethodError in CommentController#create
undefined method `product_id' for nil:NilClass
I keep getting this error..
does anyone know why this occur
Veryy Good
Thanks so much for your good job, Ryan. I really enjoy your railscasts, and I'm learning them one by one from the beginning these two days. I hope to learn all of them soon.
A suggestion just FYI: it'll be great (especially for newbies) to put Rails versions there for some railscasts which might use deprecated APIs or ways (if any).
Thank you for writing. nice sharing
thanks
asd
thanks
thanks
Thanks for the useful tip
Thank you friends,
OK body.
Thanks
asdasasdasd
thanksss you very very much
qqqsd
very nice
thanks
very good tnks
very nice
birlik otomasyon sistemleri
Does this hold good for rails 3?
I really enjoy your railscasts, and I'm learning them one by one from the beginning these two days
I need to see the code in order to determine what's wrong. But here's not the best place to post that. InsteadI recommend making a thread over
Thank you so much
Thanks Eric Baker for comments no. 38 and 39. Saved my day..
If it helps anyone, this is what I ended up with in my RJS file - assuming I had hidden 'folder_notice_success' and 'folder_notice_fail' divs above the form and I had a simple form that could only possibly return one error. I need to clean this up (and I have no idea what I'm doing), but it seems to work great. I'm also calling a Javascript function in the view that uses and AJAX call to refresh a list of folders (since this is all from a form_remote that creates a new folder). But, I'm only calling that function if the @folder.save was successful:
CONTROLLER:
respond_to do |format|
if @folder.save
flash[:notice] = "New folder successfully created!"
format.html { redirect_to 'new' }
format.js
else
flash[:error] = "doesn't matter right now as long is something is here."
format.html { redirect_to 'new' }
format.js
end
end
RJS FILE:
if !flash[:notice].blank?
page[:folder_name].value = ''
page.replace_html :folder_notice_success, flash[:notice]
page[:folder_notice_success].show
page.delay(3) do
page[:folder_notice_success].hide
end
page << "folder_lookup()"
elsif !flash[:error].blank?
page.replace_html :folder_notice_fail, 'ERROR: ' + @folder.errors.to_xml
page[:folder_notice_fail].show
page.delay(4) do
page[:folder_notice_fail].hide
end
end
flash.discard
Thanks a lot Ryan. Is the source code available for this full implementation ? otherwise it would be better to show the source code from where your started this tutorial. Thanks in advance Ryan