This is a tutorial for ajax use with Rails conventions. For illustrative purposes, we’ll build a single-page Product list app.
About Ajax
Ajax (Asynchronous JavaScript and XML) is used as a mechanism for sending and retrieving data asynchronously (in the background). While XML can certainly be used with ajax, it is not limited to this format. The JSON format, for example, is more commonly used today, especially in the Rails community. There are significant advantages in using Ajax, which include better user interactivity. Ajax allows content on a page to be updated without having to re-render the entire page, making it a “seamless” experience.
Create a New Product on the Index Page
Before we start, let’s take a quick look at our schema so that we know what we’re working with:
1 2 3 4 5 6 7 8 |
|
After creating a Product model and then create some products to play with, our Product Controller should look like this:
1 2 3 4 5 |
|
Instead of creating new.html.erb
, let’s add a button somewhere on our index.html.erb
that users can use to display a hidden form:
1 2 3 4 5 |
|
Here we pass the remote: true
option to disable the default Rails mechanism that would have otherwise navigated us to /products/new
.
Before moving on, let’s quickly revisit our Product Controller and set it up to create new products with ajax:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
I have removed the index action because I created a before_action
filter that creates the @products
instance variable for us automatically. Because we no longer have any logic in our index action, it is not necessary. Rails will automatically render the correct template, even without the presence of the action.
Noteworthy here is the respond_to
method invoked near the top that will allow us to render both html and javascript responses with all of our controller actions. The respond_to method provides the ability to respond to the requests with many formats(i.e. csv, xml, etc…). This can be done for one or all actions in a controller. If we wanted to provide json only in our index action, we would write something like this:
1 2 3 4 5 6 7 8 |
|
Now, choose a place on the index page to hide your form by passing a style attribute with the following:
1 2 3 4 5 |
|
which will hide our form when the page is initially visited.
Next, create new.js.erb
:
1 2 |
|
This is just an ERB template that generates Javascript instead of the HTML we’re used to. It basically translates to: “Find the element with an id attribute of product-form and at that point render the html in the form partial.” We typically do this in new.html.erb
with:
1
|
|
Since render is a Rails method, JavaScript doesn’t understand it and it has to be interpreted with ERB. The ‘j’ is syntactic sugar for <%= escape_javascript (render ‘form’) %>
1
2
3
4
5
<%= simple_form_for @product, remote: true do |f| %>
<%= f.input :description %>
<%= f.input :deadline %>
<%= f.button :submit %>
<% end %>
This is the ‘form’ being rendered in new.js.erb
with a remote: true
option being passed in. In our form partial, we also pass the remote: true
option that will execute an ajax POST request.
Finally, we can wrap things up by rendering our new Product list and hiding our form. This final step includes identifying where to render our list. Using the rai-jax app as an example, let’s look at what our final index.html.erb
should look like at this stage:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="row">
<div class="col-md-5 col-md-offset-1">
<h2>Products</h2>
</div>
<div class="col-md-2 col-md-offset-4">
<%= link_to new_product_path, remote: true do %>
<button class="btn btn-default">New</button>
<% end %>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-2" id="product-form" style="display:none;"></div>
</div>
<div class="row">
<div class="col-md-7 col-md-offset-1" id="products"><%= render @products %></div>
</div>
And we update our product list and hide our form with create.js.erb
:
1
2
$('#products').html("<%= j (render @products) %>");
$('#product-form').slideUp(350);
Update a Product on the Index Page
As in Part One, let’s start by visiting our Product Controller and setting it up for updates:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ProductsController < ApplicationController
before_action :all_products, only: [:index, :create, :update]
before_action :set_products, only: [:edit, :update]
...
def update
@product.update_attributes(product_params)
end
...
private
...
def set_products
@product = Product.find(params[:id])
end
def product_params
params.require(:product).permit(:name, :description)
end
end
Similar to Part One, we add an edit button with a remote:
true option:
1
2
3
4
5
6
7
...
<%= link_to edit_product_path(product), remote: true do %>
<button>Edit</button>
<% end %>
…
And, finally, our edit.js.erb
and update.js.erb
are the same as our new and update templates: edit.js.erb
corresponds to new.js.erb
and create.js.erb
corresponds to update.js.erb
.
Delete a Product on the Index Page
Our final updates to our Product Controller involves us providing the destroy action:
1
2
3
4
5
6
7
8
9
10
class ProductsController < ApplicationController
before_action :all_products, only: [:index, :create, :update, :destroy]
before_action :set_products, only: [:edit, :update, :destroy]
...
def destroy
@product.destroy
end
end
When adding our delete button, two additional steps are required:
1
2
3
4
5
6
7
...
<%= link_to product, remote: true, method: :delete, data: { confirm: 'Are you sure?' } do %>
<button>Delete!</button>
<% end %>
…
First, we pass in a method: :delete
option; Second, we provide a courtesy confirmation to the user making sure they don’t delete anything by accident.
The last file we’ll create is destroy.js.erb
and it will contain one line:
1
$('#products').html("<%= j (render @products) %>");
Seriously, Rails makes ajax easy. As I mentioned above.
So far so good, That’s it!!! See ya!!! :)