Simple Ajax in Rails 5.2
I’d like to present a short and to-the-point instruction on how to add some Ajax pizazz to a simple Rails-generated page. It won’t take you more than 10 minutes.
Let say you have a standard “scaffold”-generated app where the main model is Product.
In the index.html.erb, you’ll find:
<table> <tbody> <% @products.each do |product| %> <tr> <td><%= product.name %></td> <td><%= product.category %></td> <td><%= link_to 'Show', product %></td> <td><%= link_to 'Edit', edit_product_path(company) %></td> <td><%= link_to 'Destroy', product, method: :delete, data: {confirm: 'Are you sure?'}%></td> </tr> <% end %> </tbody> </table>
Preparing for Ajax
We cut out the main part (inside tbody), remove the @products.each loop (since Rails will do that automatically later on) and put it in a partial _product.html.erb.
<tr> <td><%= product.name %></td> <td><%= product.category %></td> <td><%= link_to 'Show', product %></td> <td><%= link_to 'Edit', edit_product_path(company) %></td> <td><%= link_to 'Destroy', product, method: :delete, data: {confirm: 'Are you sure?'}%></td> </tr>
This is now the code left in index.html.erb
<table> <tbody id="mytab"> <= render @products %> </tbody> </table>
This is where the Rails magic takes place. Rails method “render” recognizes that we are rendering a collection, and that the model name is Product. It then ‘expects’ a partial with the name “_product.html.erb” and will call that partial for every item in the collection. For every call to the partial, it will pass on the item in the variable “product”. Convention before configuration! You can customize every step of course, but if you leave the defaults, this will work.
Pure magic!
Also note that I set an id on the tbody to “mytab”, since our JS-code will replace the content of that <tbody> later when we get an update. So we need to be able to identify it.
Adding jQuery
Next step is to add some Ajax. Before we do that, we will add JQuery, which is no longer included by default. You don’t HAVE to use it, but I prefer to keep it.
Add jquery-rails to your Gemfile and bundle. Then add //= require “jquery” to application.js.
Adding a handler for the Ajax call
must now add an JS entry point in the controller. You can do this in many ways, but to keep it ease, create a simple method called filter.
def filter end
Add this to config/routes.rb
get '/filter', :to => "products#filter"
Rails will generate “filter_path” plus some other helpers from this DSL spec.
The under views/products, create an empty file called filter.js.erb. This is the javascript rendering code,.
Call using Ajax
Back in index.html.erb, we add some code that calls the controller using AJAX. There are multiple ways we could do this with a remote-enabled form, but I have decided to use one simple checkbox, which when clicked, will call the “filter” method and return a subset of the data as JSON for the JS-code to render.
Add this code to index.html.erb:
<%= label_tag "FILTER PRODUCTS" %> <%= check_box_tag 'special', '1', 1, { onchange: "$(this).data('params', 'special=' + this.checked)", data: { url: url_for(action: :filter), remote: true, method: :get } } %>
This code will call the “filter” method every time the checkbox is changed.
Handling the call
You can check the params[“special”] which will be “1” when checked and nil when unchecked to do some arbitrary selection into @products.
def filter if params["special"] # select something into @products else #products = Product.all end end
Rendering the output in product.js.erb
Since “filter” is called with type JS, Rails will render the output using product.js.erb which will contain this nugget: (take a deep breath now!)
$("#mytab").html('<%= j render @products %>');
It’s actually not that complicated!
Step one is that the ERB code is executed. “j render @products”. “j” just escapes the output from render @products.
after the ERB step, you code will actually be like:
$("#mytab").html('lots and lots of HTML created by the Rails partial view');
By use of JQuery, we then replace the content of #mytab with the generated content, using the -html() call.
You could of course have more complicated code here, that looks up a particular piece of the DOM, maybe identified with an ID (id=”product-23416″) that you could lookup and replace with your newly generated HTML. Perhaps also do some visual effects while chaging the data.
Now when you click the checkbox, there will be an Ajax call to the /filter entrypoint. Rails will recognize the JS call, and return a jQuery code block with the rendered HTML which will replace the #mytab section in your DOM.