+1

Ajax in RoR

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. Now, I will show you the basics of using AJAX in your Rails application.

1. Ajax (not use remote true)

  • The first you need 2 models.
app/models/chat_group.rb
class ChatGroup < ActiveRecord::Base
has_many :chat_messages
end
app/models/chat_message.rb
class ChatMessage < ActiveRecord::Base
belongs_to :chat_group
belongs_to :user
end
  • In controller, we will have request.xhr? And catch params[:message].present? app/controllers/chat_groups_controller.rb:
class ChatGroupsController < ::AuthenticatableController
def show
@chat_group = ChatGroup.find params[:id]
if request.xhr?
if params[:message].present?
@new_message = ChatMessage.new(user_id: current_user.id,
chat_group_id: params[:chat_group_id], message: params[:message])
if @new_message.save
@chat_messages = @chat_group.chat_messages
end
render partial: "chat_messages"
end
end
@new_chat_message = ChatMessage.new
@chat_messages = @chat_group.chat_messages
end
end
  • In view, we will show all messages in div with id = “scroll_view” like that. app/views/show.rb:
<%= @chat_group.name %>

<% if @chat_messages.present? %>
<% @chat_messages.reverse_each do |message| %>
<%= render “message”, message: message %>
<% end %>
<% end %>
<%= render “new_chat_message” %>
<%= render “script_show” %>

We need partial _message.html.erb to render when append with ajax.

_message.html.erb

“>
<% if message.user.present? %>
<%= link_to message.user.full_name, user_path(message.user) %>
(<%= message.created_at.in_time_zone(+7).try(:strftime, “%H:%M %e/%m”)%>):
<% if message.to_user_ids.present? %>
TO :
<% to_users = User.find_by_ids message.to_user_ids.split(“,”) %>
<% to_users.each do |to_user| %>
<%= link_to to_user.full_name, user_path(to_user) %>
<% unless to_user == to_users.last %> ,<% end %>
<% end %>
<% end %>
<%= message.message.gsub(“\n”,”
“).html_safe %>
<% end %>
  • From write message and send, it have button id: "submit_chat_message" _new_chat_message.html.erb
<%= form_for @new_chat_message, url: "#", method: :get do |f| %>
<%= f.text_area :message, placeholder: "Enter your message ...", class: "chat_comment" %>
<%= f.submit "SEND", class: "btn btn-primary send_comment" , id: "submit_chat_message"%>
<% end %>
  • Script use in view, you can write in file_name.html.erb or write in other file and then render in file view will using it. In there we will process event click on button “ submit_chat_message” script_show.html.erb
// <![CDATA[
$(document).ready(function(){
$("#submit_chat_message").on("click", function(event) {
event.preventDefault();
var to_user_ids = $("#chat_message_to_user_ids").val();
if(to_user_ids != null){
to_user_ids = to_user_ids.join();
}
$("#chat_message_to_user_ids").val("");
if($("#chat_message_message").val() != ""){
    $.ajax({
        type: "GET",
        url: "",
        data: {chat_group_id: "", message: $("#chat_message_message").val(), to_user_ids: to_user_ids},
        success: function(data){
        if (data != null){
            $("#chat_message_message").val("");
            $("#scroll_view").html(data);
            $("#scroll_view").scrollTop($("#scroll_view")[0].scrollHeight);
        }else{
            alert("error internet ?");
        }
    }
});
}
});
});
// ]]>

Ok, now you can run server and test Ajax not using remote:true.

2. Ajax (use remote true)

In this example, I will use twitter bootstrap to add some styling and functionality to our app, you need setup gem boostrap to start. After 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.

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

Then rake db:migrate to have model. Now lets open the model 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

Create some data to example by copy and paste the code below into your seeds.rb file. (db/seeds.rb)

Product.delete_all

Product.create!([
    {id: 1, name: "Nokia N3", price: 230},
    {id: 2, name: "Samsung Note 3", price: 304},
    {id: 3, name: "Iphone 6", price: 239.95},
    {id: 4, name: "Blackbery 10", price: 99.95}
])

Run the seed command 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.

class ProductsController < ApplicationController
respond_to :html, :js`

    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

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>
<%= render "index" %>
<table class="table table-bordered table-striped">
    <thead>
    <tr>
    <th>Name</th>
    <th>Price</th>
    <td></td>
    <td></td>
    </tr>
    </thead>
</table>
</div>
<div id="product-modal" class="modal hide 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/product/_index.html.erb

<% @products.each do |product| %>

    <%= product.name %>
    <%= number_to_currency product.price %>
    <%= link_to "Edit", edit_product_path(product), remote: true, class: "btn" %>
    <%= link_to "Delete", product_delete_path(product), remote: true, class: "btn btn-danger" %>

<% end %>

Great and now 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.

<%= form_for @product, remote: true, html: { class: "form-horizontal", style: "display:inline;" } do |f| %>`
<div class="modal-body">
<div class="control-group">

<%= f.label :name, class:"control-label" %>
<div class="controls"><%= f.text_field :name %></div>
</div>
<div class="control-group">

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

Then create a _new.html.rb file for products and add the following code.

<div class="modal-header">

### New Product

</div>
<%= render "form" %>

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

<div class="modal-header">

### <%= "Editing #{@product.name}" %>

</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 and edit.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")

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:

<% if @product.errors.any? %>
    <% @product.errors.full_messages.each do |message| %>
    $("ul.errors").append($("").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 and update.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" %>

app/views/products/update.js.erb

<%= render "save" %>

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.

<div class="modal-header">

### Delete Product

</div>
<div class="modal-body">

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", data: { dismiss: "modal" } %></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")

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" %>

OK, now we run server to see which we do. That is two ways using ajax in rails, thanks for reading.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.