Basic AJAX in Ruby on Rails

This article will teach you the basics of AJAX in Ruby on Rails.


Published on:October 13, 2013

Introduction

Quite often in the past, we had to result to a ton of different javascript to perform many different AJAX operations, luckily Ruby on Rails makes AJAX structured and easy. This article will teach you the basics of using AJAX in your Rails application.

In this example, we will use twitter bootstrap to add some styling and functionality to our app. This step is completely optional, you can use your own 3rd party styles and functionality. The main thing we will be using from the bootstrap library is the modal dialog. You can just as easily use the one from JQuery UI or fancybox. Add the bootstrap gem to your gemfile.

Gemfile:

gem 'bootstrap-sass', '~> 3.3.5.1'

Next, run a bundle install.

Terminal Commands:

bundle install

Next, create a file in your assets stylesheets folder called bootstrap_config.scss and add the following code.

app/assets/stylesheets/bootstrap_config.scss:

@import "bootstrap-sprockets";
@import "bootstrap";

Next, add a require for the bootstrap javascript files to your application.js file.

app/assets/javascripts/application.js:

//= require bootstrap

Now that we have bootstrap set up, we need to set up an example model, controller, and some example data. In this sample project we will have a single model called Product. Run the commands below to create this model.

Terminal Commands:

rails g resource Product name price:decimal{12,2}
rake db:migrate

Now lets open the modal and add a few validations. Modify your product model so it looks like the code listed below.

app/models/product.rb:

class Product < ActiveRecord::Base
  validates :name, presence: true
  validates :price, presence: true,
            numericality: true,
            format: { :with => /\A\d{1,4}(\.\d{0,2})?\z/ }
end

Great, now let's populate the database. Copy and paste the code below into your seeds.rb file.

db/seeds.rb:

Product.delete_all

Product.create!([
  {id: 1, name: "Nintendo Wii U Premium", price: 250},
  {id: 2, name: "XBox 360 250GB", price: 250},
  {id: 3, name: "Playstation 3 500 GB", price: 239.95},
  {id: 4, name: "Nintendo Wii", price: 99.95},
  {id: 5, name: "Nintendo 3DS", price: 174.95}
])

Now lets run the seed command.

Terminal Commands:

rake db:seed

Now that we have some data, it's time to add CRUD (create, read, update, delete) operations to the products controller. Modify your products controller so that it looks like the code listed below.

app/controllers/products_controller.rb:

class ProductsController < ApplicationController
  
  def index
    @products = Product.all
  end

  def show
    @product = Product.find(params[:id])
  end

  def new
    @product = Product.new
  end

  def create
    @products = Product.all
    @product = Product.create(product_params)
  end

  def edit
    @product = Product.find(params[:id])
  end

  def update
    @products = Product.all
    @product = Product.find(params[:id])
    
    @product.update_attributes(product_params)
  end

  def delete
    @product = Product.find(params[:product_id])
  end

  def destroy
    @products = Product.all
    @product = Product.find(params[:id])
    @product.destroy
  end

private
  def product_params
    params.require(:product).permit(:name, :price)
  end
end

Now, lets set up our routes. We need to add an extra get method for our 'delete' view. This delete view displays a confirmation before deleting an item. In addition, we need to set our root to our products controller. Open your routes file and modify it so that it looks like the code listed below.

config/routes.rb:

AjaxExample::Application.routes.draw do
  resources :products do
    get "delete"
  end

  root to: "products#index"
end

Now lets create an index view. Create a products index.html.erb file and add in the code listed below.

app/views/products/index.html.erb:

<div class="container">
  <div class="well">
    <%= link_to "New Product", new_product_path, remote: true, class: "btn btn-primary" %>
  </div>
  <div class="new-product"></div>
  
  <table class="table table-bordered table-striped">
    <thead>
      <tr>
        <th>Name</th>
        <th>Price</th>
        <td>&nbsp;</td>
        <td>&nbsp;</td>
      </tr>
    </thead>
    <tbody class="product-index">
      <%= render "index" %>
    </tbody>
  </table>
</div>
<div id="product-modal" class="modal fade"></div>

The remote: true tells rails that we wish to use AJAX for those hyperlinks. Next, we need to create the _index.html.erb partial. This partial actually renders the product list. It is used by many different views, so it needs to be separate from the actual index view. Create the _index.html.erb partial and add the code listed below.

app/views/products/_index.html.erb:

<% @products.each do |product| %>
  <tr>
    <td><%= product.name %></td>
    <td><%= number_to_currency product.price %></td>
    <td><%= link_to "Edit", edit_product_path(product), remote: true, class: "btn btn-default" %></td>
    <td><%= link_to "Delete", product_delete_path(product), remote: true, class: "btn btn-danger" %></td>
  </tr>
<% end %>

Okay, now it's time to set up our forms. First, we will need to create a _form.html.erb partial. This file will hold our form that is used for creating and editing our products. Create the _form.html.erb partial and add in the code listed below.

app/views/products/_form.html.erb:

<div class="modal-dialog">
  <div class="modal-content">
    <%= form_for @product, remote: true, html: { style: "display:inline;" } do |f| %>
      <div class="modal-body">
        <ul class="errors"></ul>

        <div class="form-group">
          <%= f.label :name, class:"control-label" %>
          <%= f.text_field :name, class: "form-control" %>
        </div>
        <div class="form-group">
          <%= f.label :price, class: "control-label" %>
          <%= f.text_field :price, class: "form-control" %>
        </div>
      </div>
      <div class="modal-footer">
        <%= f.submit class: "btn btn-primary" %>
        <%= link_to "Cancel", "#", class: "btn", data: {dismiss: "modal"} %>
      </div>
    <% end %>
  </div>
</div>

Great, now create a _new.html.rb file for products and add the following code.

app/views/products/_new.html.erb:

<div class="modal-header">
<h3>New Product</h3>
</div>
<%= render "form" %>

Ok, now create an _edit.html.rb file for products and add the following code.

app/views/products/_edit.html.erb:

<div class="modal-header">
  <h3><%= "Editing #{@product.name}" %></h3>
</div>
<%= render "form" %>

Great, now we need to create a new.js.erb view for products. This file contains javascript and will get returned whenever someone clicks the new product link on index. Rails automatically does this whenever remote is set to true. Create the new.js.erb view and add in the code listed below.

app/views/products/new.js.erb:

$("#product-modal").html("<%= escape_javascript(render 'new') %>")
$("#product-modal").modal("show")

Now lets do the same thing for edit.js.erb. Create an edit.js.erb view for products and add the following code.

app/views/products/edit.js.erb:

$("#product-modal").html("<%= escape_javascript(render 'edit') %>")
$("#product-modal").modal("show")

Next, we need to create a partial that will be shared between the create, update, and destroy js files. This file will determine if the product was successfully saved. If so it will hide the modal dialog, otherwise it will list the errors. Create the _save.js.erb file and add in the code listed below.

app/views/products/_save.js.erb:

$("ul.errors").html("")
<% if @product.errors.any? %>
  <% @product.errors.full_messages.each do |message| %>
    $("ul.errors").append($("<li />").html("<%= message.html_safe %>"))
  <% end %>
<% else %>
  $(".product-index").html("<%= escape_javascript(render 'index') %>")
  $("#product-modal").modal("hide")
<% end %>

Next we will add the create.js.erb view. This view is returned when the user actually clicks the submit button on the form. This view will use the save partial we wrote earlier.

app/views/products/create.js.erb

<%= render "save" %>

Now, lets add the update.js.erb file. This view will also use the save partial we wrote earlier.

app/views/products/update.js.erb:

<%= render "save" %>

Phew! We are almost there. Now lets create a view that will let us give the user a confirmation prompt prior to deleting a product. First, create a partial called _delete.html.erb and add in the code listed below.

app/views/products/_delete.html.erb:

<div class="modal-dialog">
  <div class="modal-content">
  <div class="modal-header">
    <h3>Delete Product</h3>
  </div>
  <div class="modal-body">
  <ul class="errors"></ul>
  Are you sure you wish to delete <b><%= @product.name %></b>?
  </div>
  <div class="modal-footer">
    <%= link_to "Yes, Delete This Product", product_path(@product), method: :delete, remote: true, class: "btn btn-danger" %>
    <%= link_to "No, Please Don't", "#", class: "btn btn-default", data: { dismiss: "modal" } %>
  </div>
  </div>
</div>

Now, we need to create the delete.js.erb javascript view itself. Create the delete.js.erb file and add in the code listed below.

app/views/products/delete.js.erb:

$("#product-modal").html("<%= escape_javascript(render 'delete') %>")
$("#product-modal").modal("show")

Almost there! Now add a javascript view called destroy.js.erb and add the code listed below.. Once again, this will get returned when the product is destroyed. It calls the save partial we wrote earlier.

app/views/products/destroy.js.erb:

<%= render "save" %>

Phew! Now if you start and navigate to your Rails Development Server you will notice that everything is done via ajax, in this case via a bootstrap modal dialog. That is all there is to AJAXifying your app, thanks for reading!