AJAX on Rails: Ajaxified Create and Destroy Actions for has_many / belongs_to related Models

AJAX on Rails: Ajaxified Create and Destroy Actions for has_many / belongs_to related Models

It’s been about 8 months since I made the switch from PHP to Ruby. Now about 80% of all my web programming work is done in Ruby, and I’m much more quickly able to build complex applications.

HOWEVER: I hate Ruby. I love Rails. Maybe it’s because I originally learned to code in Java and I’m still¬†uncomfortable with Ruby’s syntax? IDK. W/E. Rails is great.

Today’s post is about how to implement some quick AJAX functionality on Rails so that simple nested models can be Created and Destroyed without having to refresh the page.

The Setting for Our Story

I recently built a CMS for diseases, mutations and a whole other lot of information for Recombine. One of our models is the Disease model:

disease.rb

...
has_many :references

def to_param
     permalink
end
...

Notice that I have defined to_param as the permalink for the disease model. I’ve decided that I want the URLs to be custom as I’ve previously explained how to do. The model we will be using AJAX on that belongs to a Disease is the Reference (as in a reference within scientific literature) model:

reference.rb

...
belongs_to :disease
...

Finally, we can specify the resources available via the Routes file:

routes.rb

...
resources :diseases do
     resources :references, :o nly => [:create, :destroy]
end
resources :references, :o nly => [:create, :destroy]
...

Notice that we are only adding the Create and Destroy actions for our References Controller. With this stage set, our goal is to establish our application so that, for a given disease, all disease information and associated references are viewable from the views/diseases/view.html.erb file, and that editing the disease information, or adding/removing associated references, can all be accomplished from the views/diseases/edit.html.erb file. Note: I have not included a description of the migrations/table/columns structure. You can assume whatever fields exist, and know with certainty that the reference table has a disease_id column.

Setting Up Our Controllers

The Diseases Controller is going to be fairly typical with most RESTful actions available. The References Controller, is only going to have two RESTful actions, and both will only respond to javascript requests.

diseases_controller.rb

class DiseasesController < ApplicationController

...

     before_filter :authenticate, :except => [:index, :show]
     before_filter :admin_user, :except => [:index, :show]

     def index
          @diseases = Disease.all
          @title = "All Diseases"
     end

     def show
          @disease = Disease.find_by_permalink(params[:id])
          @references = Disease.references
          @title = Disease.name
     end

     def edit
          @disease = Disease.find_by_permalink(params[:id])
          @references = Disease.references
          @title = Disease.name
          @reference = Reference.new
     end

...

end

Note that the other RESTful actions are all there, but I’m only displaying the Show and Edit actions as they are all we really are working with for this post. Also note the instance variable @reference is declared in the Edit action so that we may create new references from the Disease Edit page. Now for the References Controller:

references_controller.rb

class ReferencesController < ApplicationController

...

     before_filter :authenticate
     before_filter :admin_user

     def create
          @reference = Reference.new(params[:reference]) #Params from form
          @reference.save
          @response = @reference.errors #Errors
          @references = Reference.where(:disease_id => params[:reference][:disease_id]).all
          @disease = Disease.find_by_id(params[:reference][:disease_id])
          respond_to do |format|
               format.js {}
          end
     end

     def destroy
          Reference.find_by_id(params[:id]).destroy
          @disease = Disease.find_by_id(params[:disease_id])
          @references = @disease.references
          respond_to do |format|
               format.js {}
          end
     end

...

end

Notice that in the Create action, a reference is created from the params[:reference] parameters. These are going to be passed to our controller from our AJAXified References form located in the views/diseases/edit.html.erb file. Also notice that we re-delcare the @diseases and @references instance variables in the create action. This is important for when we update our _reference_list.html.erb partial within the main page.

In the Destroy action, we are simply sending along the id of the reference that we wish to destroy via AJAX. However, in order to properly re-render the (forthcoming) _reference_list.html.erb partial, we also need to send over a disease_id parameter so that we can then re-declare the @disease and @references instance variables for the partial. If you didn’t follow that, don’t worry, it will become clear once we see the views.

The Views to Put it Together

The views/diseases/show.html.erb view is going to be a pretty standard run through the @disease and @references instance variables. However, the views/diseases/edit.html.erb is where all of the AJAX magic will occur. Let’s look at the views/diseases/edit.html.erb view first:

views/diseases/edit.html.erb

#Make the form_for function for the disease model data
<%= form_for @disease do |f| %>
...
<%= f.submit :value => "Update Disease" %>
<% end %>

#Render the reference list
<ul class="reference_list">
     <%= render 'references/reference_list' %>
</ul>

#Make the new reference form
<%= form_for @reference, :remote => true do |f| %>
     <%= f.hidden_field :disease_id, :value => @disease.id %>
     <%= f.text_area :info, :class => "reference_info" %>
     <%= f.submit :value => "Add Reference" %>
<% end %>

You can see that we have passed the hidden field disease_id on the reference form as we had anticipated needing in the controller. Before we take a look at what submitting the form does, let’s first look at our reference list partial:

views/references/_reference_list.html.erb

<% @references.each do |r| %>
     <%= r.info %>
     <%= link_to reference_path(r, :disease_id => @disease.id), :method => :delete, :confirm => 'Delete this Reference?', :remote => true do |l| %>Delete<% end %>
<% end %>

Notice that we have generated a link_to tag for each reference that marks a Delete action for that reference. The Delete Action in the References Controller will remove the associated reference upon clicking the link (after clicking okay to associated confirmation message). Now only two thins are left to do: views/references/create.js.erb and views/references/destroy.js.erb. Javascript must be used to update the reference list each time a create or destroy action is called from the views/diseases/edit.html.erb view.

views/references/create.js.erb

//The first line updates the reference list (with the new Reference).
$('ul.reference_list').html("<%= escape_javascript(render('references/reference_list')) %>");

//This second line removes the text that was within the textarea (looks nicer and avoids duplication).
$('textarea.reference_info').val("");

views/references/destroy.js.erb

//Here we only need to re-render the reference list after the destroy action is done.
$('ul.reference_list').html("<%= escape_javascript(render('references/reference_list')) %>");

Whew. That took a lot to accomplish such a simple concept. Not sure it was the best way to go about doing so, but it’s up and working. Would love to hear from anyone if there’s a simpler way within rails to accomplish this sort of effect. Good luck to those playing around with AJAX on Rails!

3 Comments

  1. Big Thanks!! Its article help me in similar situation.

  2. You post very interesting articles here. Your page deserves much bigger audience.
    It can go viral if you give it initial boost, i know very useful service that
    can help you, just type in google: svetsern traffic tips

  3. If you are interested in topic: earn online without investment questionnaire form –
    you should read about Bucksflooder first

Leave a Reply

Spam Protection by WP-SpamFree