RJS templates in Rails or Ajax with ease a 101 .rjs walkthrough
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...
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
This was awesome, thanks.