fallenrogue.com

RJS templates in Rails or Ajax with ease a 101 .rjs walkthrough

It’s amazing to me the amount of things in the Rails community go virtually unnoticed. Even those items that do make it to the front page aren’t really followed up with nice real world implementation samples. So, developers continue to do what they’ve done and that’s fine, to a point. Rails seems to breed this more than other frameworks I’ve used because they are young and constantly evolving. One of these features is the RJS template. Most Rails dabblers I talk to have heard of them but haven’t seen the point or power of implementing them.

In talking with someone the other day online, they were trying to alter something after an Ajax call and emailed me the script that crammed 3 prototype/scriptaculous calls in the :update property of a link_to_remote method call. If I can find the email, I’ll post it but, for reasons I’ll show now, I quickly said: “you’re crazy! Use the RJS template!” My friend said. “Oh, yeah, I heard about them but I didn’t get it so I did this, I mean, it works, so that’s cool.”

No. No it is not cool at all. Why do something the long way when there is a better way waiting for you right around the corner? Exactly. Let’s use some RJS together! I encourage others to share their techniques in the comments. Let’s all get on the same page, first. That will make all of this make a lot more sense, cool? cool.

Start with a New Project


Open that terminal (or cmd window, you poor unfortunate Windows user.) and throw down the new project command and create a couple of mysql dbs. You’re welcome to use whatever db you want, I’m using MySQL.
rails rjsdemo
mysqladmin -u YOURUSERNAMEHERE create rjsdemo_development
mysqladmin -u YOURUSERNAMEHERE create rjsdemo_test

We’re already doing well, aren’t we? Let’s create a model and a controller to play with… or else we have nothing to call! How about blogging system? Sure! In this system we’re going to allow users to create entries, allow others to comment and rate those entries. It’s those things that we’ll toss the RJS magic in! So, either get your copy and paste on or start typing. As this is a 101 level tutorial, I recommend typing, but sometimes I just want to hit pay-dirt! Either way… let’s continue.

 
script/generate model entry
script/generate model comment
script/generate model rating

Ok, now, I’m not adding users or permissions or anything fancy like that. This is about RJS and it’s gonna take long enough to get there as it is. Enough talk, more code.
script/generate controller entries index new edit view delete create update
script/generate controller comments new delete
script/generate controller ratings rate_entry

But wait, Rogue! Couldn’t I have scaffolded them? Yes. I, personally, am not a fan of the scaffold. I write less code than the scaffold and I hate cleaning up after it. But if you’re a scaffolder… enjoy. Change controller to scaffold and you’re all set. Let’s set up the migration scripts. If you’re not using migration scripts for your database you are going to start right now. That’s how awesome they are. They are the Brussels sprouts of Rails. You’re going to use em and you’re going to like em, period. That’s another article…for now, let’s make a schema.
class CreateEntries < ActiveRecord::Migration
  def self.up
    create_table :entries do |t|
      t.column :title, :string
      t.column :body, :text
      t.column :created_on, :datetime
      t.column :updated_on, :datetime
    end
  end

  def self.down
    drop_table :entries
  end
end

class CreateComments < ActiveRecord::Migration
  def self.up
    create_table :comments do |t|
      t.column :author, :string
      t.column :body, :text
      t.column :created_on, :datetime
      t.column :entry_id, :integer
    end
  end

  def self.down
    drop_table :comments
  end
end

class CreateRatings < ActiveRecord::Migration
  def self.up
    create_table :ratings do |t|
      t.column :entry_id, :integer
      t.column :rating, :integer
    end
  end

  def self.down
    drop_table :ratings
  end
end

go back to the Terminal and commit your fantastic new schema!
rake db:migrate

Ooooo now we’re getting somewhere. Let’s make the models work.
class Entry < ActiveRecord::Base
  has_many :comments
  has_many :ratings
end

class Comment < ActiveRecord::Base
  belongs_to :entry
end

class Rating < ActiveRecord::Base
  belongs_to :entry
end

You’d want to add validation and stuff like that…but again, we need to get to RJS. First, let’s make the controllers work.
class EntriesController < ApplicationController

  def index
    @entries = Entry.find(:all, :order=>'created_on DESC')
  end

  def new
    @entry = Entry.new
  end

  def create
    @entry = Entry.create(params[:entry])
    flash[:notice] ="Entry created." 
    redirect_to :action=>"index" 
  end

  def edit
    @entry = Entry.find(params[:id])
  end

  def update
    @entry = Entry.find(params[:id])
    if @entry.update_attributes?
      flash[:notice] = "Entry updated." 
    end
    redirect_to :action=>"index" 
  end

  def view
    @entry = Entry.find(params[:id])
  end

  def delete
    Entry.find(params[:id]).destroy
    redirect_to :action=>"index" 
  end
end

class RatingsController < ApplicationController

  def rate_entry
    @entry= Entry.find(params[:id])
    @rating = Rating.create(params[:rating])
    @entry.ratings << @rating
    render :layout=>false
  end
end

class CommentsController < ApplicationController

  def new
    @entry = Entry.find(params[:id])
    @comment = Comment.create(params[:comment])
    @entry.comments << @comment
    render :layout=>false
  end

  def delete
    Comment.find(params[:id]).destroy
    render :layout=>false
  end
end


Is this enough code for you guy, yet? Good! Now, I could go through all the glue code in the rhtml erb templates to make the calls and to create the entries but c’mon, that would bore the crap out of you. Afterall, you’re interested in the RJS magic, aren’t you? Right! So, without further ado, let the magic begin!

You created 3 views that you don’t really need. “comments/delete.rhtml, comments/new.rhtml and ratings/rate_entry.rhtml�?? well, you need them, just not as they are not, change their types from .rthml to .rjs

That’s it. Up to this point that is the only thing that you’ve done differently thank calling a regular controller method and subsequent view. That’s not hard at all. I like to think of it this way, when you’re calling a new method for a “post-back�?? action and you want to go Ajax, do it like you would any other method on a controller. Keep the MVC separation and just change the rhtml template to an rjs template. That way you’ll know where to look when you forget what’s going on.

So, in your RJS template you have context to a powerful object: “page�??. Page is there to give you access to the DOM for all the fun page manipulation you can think of. In my example (check out the entries/view.rhtml for the DOM nodes referenced in the code) I’m showing a element to give a message, I’m updating the “count�?? properties (count or rating) and finally adding the newly created comment. (Please note, that I would do this with partials but I have left them out just to show a more exact approximation of what I’m doing.) So, for the “new�?? method on the Comment controller I have this code in the new.rjs template.
page["comment_count"].replace_html pluralize(@entry.comments.count, "comment")
page["notice"].replace_html('comment added. This message will self-destruct in 5 seconds.')
page["notice"].show
page.insert_html "after", "comments", "<strong>"+@comment.author+"</strong>:"+@comment.body+"<hr>" 
page.visual_effect "highlight", "comments" 
page.delay(5) do
  page.visual_effect "fade", "notice" 
end

In this example, you replace the html that states the total number of comments, you update the “notice�?? message and show it. then you add the new comment to the comment collection and finally, add a ticking countdown to remove the message that you showed the good people who took the time to comment at your site. No messy code, no placing crap all over the place, just one template that replaced one that you would have had anyway! It’s a great way to add some automagical ajax to your site and I recommend you check it out!

So, that’s it. I know that it’s a little 101 for most of my readers but I’ve had enough emails about the topic that I thought it was time to post a tutorial. Please feel free to download the source-code with like all the samples on the site are free to modify and share and do with as you please no credit given is necessary but greatly appreciated. :)

Also, feel free to make comments below and if you see any errors or any updates you’d like or questions you’d like to ask I’d be happy to answer. Let’s not get off topic with “I can do that in Django [fill in your framework of choice] this way!�?? I don’t care. Also, I cut corners with partials, tests, validation and other things. I know. I wrote this once and didn’t edit it so let’s not split hairs on the silly stuff and keep the errata to things that will help the community at large. :) with that, enjoy!

Download the Rails Project code

articleStats

Here are some silly little facts about this RJS templates in Rails or Ajax with ease a 101 .rjs walkthrough...

It was written by fallenrogue over 2 years ago.
It has 10364 letters in it.
It has 1464 words in it.
It has a total of 29 comments in all.
So far Jon has the last word!

article Links

These are the links that appear in this article. They probably don't make sense out of context... but just in case. :)

Download the Rails Project code
 

The other stuff...

What the kids are saying...

over 2 years ago Mark Haliday said...

This was awesome, thanks.

over 2 years ago fallenrogue said...

no problem! thanks for checking it out.

over 2 years ago gagel said...

good read

over 2 years ago ecentinela said...

thanks for this clear tutorial!

over 2 years ago newbie said...

Hey tried this little tutorial out. Two things to keep in mind. Running rails 1.1.4 and took the files you offered and downloaded them ot test out.

So far I'm able to view the add an entry. However when I go and click on an entry from this URL.
http://localhost:3000/entries

I'm taken to this URL
http://localhost:3000/entries/view/5

Where I get this error...

NoMethodError in Entries#view

Showing app/views/entries/view.rhtml where line #19 raised:

undefined method `author' for {:url=>{:action=>"new", :controller=>"comments", :id=>4}}:Hash

Am I missing something?

This looks tutorial looks interesting please bare with me I am a newbie.

Look forward to any feedback.

over 2 years ago fallenrogue said...

Welcome newbie! Happy to have you here! Your error has to do with your version of Rails. If you go to line 19 on the file views/entries/view.rhtml you'll notice the following line <br>

remote_form_for :comment, :url=>{:controller=>"comments", :action=>"new", :id=>@entry.id } do |f| <br>

remote_form_for is a ajax version of the method "form_for" the original syntax for "form_for" required 3 arguments instead of 2. This was changed in a later version of rails which is what I based my tutorial on. <br>

So you have 2 options, update to the latest version of Rails 1.1.6 (sudo gem update rails) or change that line(19) to look like this... <br>

remote_form_for :comment, Comment.new, :url=>{:controller=>"comments", :action=>"new", :id=>@entry.id } do |f|<br>

Please note that this means that line 8 now needs to look like this<br>

remote_form_for :rating, Rating.new, :url=>{:controller=>"ratings",:action=>"rate_entry", :id=>@entry.id } do |f|<br>

for it to work in Rails 1.1.4 as well. I hope that helps! Let me know if you have any other questions!

over 2 years ago newbie said...

Thanks for the reply. I updated to rails 1.1.6 and everything looks good.

Awesome tutorial its great to see how this all works. These little facts are great to know.

over 2 years ago rifraf said...

Thanks for the article.
Just to prove that I typed in what you wrote, I can happily report that the lines <code>render :action=>"index"</code> need to be replaced with <code>redirect_to :action=>"index"</code>, as in the downloaded version... Can I claim my prize?

over 2 years ago fallenrogue said...

LOL! You win rifraf!!! :) I'll update the article now. :)

over 2 years ago Mike SEOG.net said...

Thanks for the tutorial, it is much appreciated to see some useful examples on RJS coming out. I saw the blog entry on ROR main site but there haven't been as many practical examples out there!

over 2 years ago fallenrogue said...

Thanks Mike, I plan to do some higher level examples in the future to give a taste of what's really possible so please keep checking back! :)

about 1 year ago David Jones said...

def rate_entry
@entry= Entry.find(params[:id])
@rating = Rating.create(params[:rating])
@entry.ratings << @rating
render :layout=>false
end

Should just be....

def rate_entry
@entry= Entry.find(params[:id])
@entry.ratings.create(params[:rating])
render :layout=>false
end

about 1 year ago fallenrogue said...

Yup, that works too! nice and DRY, David! I've gotta stop writing these articles so late at night. :)

about 1 year ago Leon said...

Arshad - Here found one!

http://synthesis.sbecker.net/articles/2005/07/10/ruby-on-rails-data-grid-control

Hope that helps :)

about 1 year ago fallenrogue said...

is it me or is comment spam getting more creative?

about 1 year ago tjs said...

Hi. Thanks.

about 1 year ago jkm said...

Thanks

about 1 year ago Arshad Syed said...

Hello Leon,

Thank you very much for putting together the RJS 101 tutorial. I am your humble student now!!!!

I like your style of teaching and am sure many agree with me. I think you should offer a paid-mentoring service for all things Ruby.

I new to all things that help compose the "dynamic" website.

I am looking to build/buy a data-grid for my website. I am wondering if you could point me in the right direction.

I look forward to hearing from you.
Arshad

about 1 year ago Leon said...

Hi Arshad! Well, first of all thank you very much for your kind words. I would love to start a teaching/speaking business but am just not in a point in my life where I'm ready to focus on it. Maybe soon, though!

As far as your datagrid issue. I don't know of any dg plugins for rails but you could easily create much of the functionality pretty quickly in Rails and then create a reusable plugin for it. Tell you what, I'll do my best to free some time and whip up a datagrid on rails example in the next day or to which you, of course, can choose to use or not. :) thanks again ~Leon

about 1 year ago arpit jain said...

Hey,
I just have a page where I show a list of say courses. While creating a new course, user is redirected to course/new. Now after course is created he is redirected back to the course list where I flash the notice that "course is created successfully". How can I fade this message using RJS. Notice flashing div is defined in application layout. Please help asap.

about 1 year ago Leon said...

Hi Arpit Jain,

Well, the answer to your question is in the article. from the rjs file target the element that you'd like to fade out and call out to the delay method.

page.delay(5) do
page.visual_effect "fade", "notice"
end

That's all there is to it! Hope that helps, ~Leon

about 1 year ago dave said...

RJS file never work for me. I don't understand why not though.

My controller is spirit/base
method is new

so I put an RJS file called new.rjs in view/spirit/base. AND NOTHING. ??? I know I am missing something simple. Should I put the RJS file someplace else?

I am using netbeans debuger and I believe the RJS file is never called.

about 1 year ago dave said...

OOPS i had a new.rhtml and new.rjs thus I got the rhtml page and no rjs page

10 months ago james said...

rogue, you are awesome. thank you.

10 months ago Leon said...

thanks! :)

2 months ago name said...

Good day!,

2 months ago name said...

Hi!,

2 months ago name said...

Hi!,

18 days ago Jon said...

nice

Leave a comment
*name:
*email: (never sold or published.)
url :

©2000-2008 fallenrogue.com | Some Rights reserved.