kqb3xq
Last Updated: October 16, 2016
·
98.45K
· sebastialonso
9b0f7bbf22d65fa2bd23496973c0ba80

Rails 4: How to partials & AJAX, dead easy

Consider the basic following scenario...

A view with two columns:

  • one with links of Category model instances

  • the other empty but eager to show all the Item instances belonging to each Category.

And you want to show all of that without reloading the page. I had never played much with partials in Rails, but they are really really convenient.

So I have my index method

#items_controller.rb
def index
    @items = Item.all
    @categories = Category.all
end

For the sake of brevity, I will simplify the views. This way you can also understand the idea and apply it to your own views.

The index view contains two render calls

<!-- items/index.html.erb -->
<div class="grid">
  <%= render 'sidebar_menu' %>

  <%= render partial: 'item_grid', locals: { items: @items} %>
</div>

The category links in the sidebar_menu partial are something like the following:

<%= link_to cat.name, fetch_items_path(:cat_id => cat.id), :remote => true %>

fetch_items_path is the route that leads to our custom javascript method, which will be described next.

#config/routes.rb
...
get "/fetch_items" => 'items#from_category', as: 'fetch_items'

For more info on how to build custom routes, check Rails amazing documentation.

The :remote => true is the most important part here, it allows the whole Ajax business in the first place.

The item_grid partial looks like:

<div>
  <div id="items_grid" >
    <%= render partial: 'items_list', locals: {items: items}  %>
  </div>
</div>

The subpartial items_list just renders a list of div boxes to show our Item instances.

<% items.each do |item| %>
  <div class="item_box">
    ...
  </div>
<% end %>

Now we need the method that will do the AJAX magic. For simplicity you could have something like this:

#items_controller.rb
def from_category
    @selected = Item.where(:category_id => params[:cat_id])
    respond_to do |format|
        format.js
    end
end

Notice the type of format, there's no html view because we don't need it. We're going through JS.
Therefore, we need to create a javascript file, which will repopulate the div in the second column.

//views/items/from_category.js.erb
    $("#items_grid").html("<%= escape_javascript(render partial: 'items_list', locals: { items: @selected } ) %>"); 

Let's look at this line with care.
I'm rendering the same partial I was rendering in items#index, and the local variable for the partial is now the array of Item instances that match a given category. The difference is that I'm doing this through AJAX, so there's no need to reload the entire page.

24 Responses
Add your response

15861

Hi, thanks for the article.
I don't understand the connection between 'fetchitemspath' and the 'fromcategory' action in your items controller.
In other words can you explain "fetch
items_path is the route that leads to our custom javascript method". I do not understand how that works.
Thank you!

over 1 year ago ·
15867
9b0f7bbf22d65fa2bd23496973c0ba80

In Rails, you control the routes in the routes.rb file. I will add it to the pro tip.

over 1 year ago ·
16150
91ebd3eb18db215d033dc43ba44dae3b

Thank you for writing this, it was super helpful. Just one question, what is the purpose of the :remote => true statement at the end of the link tag?

over 1 year ago ·
16157
9b0f7bbf22d65fa2bd23496973c0ba80

As it's pointed out just beneath the routes file, the remote: true hash tell Rails that the anchor element must respond in Javascript format (triggerin the Ajax logic), instead of the usual HTML.

Note that the controller method has to respond in this format also, otherwise is of no use.

over 1 year ago ·
16815
D2daf341883a14a6bf43bf25d655af81

This is the best tutorial / documentation I've seen for this. Making the content minimal and concentrating on the mechanism was a good idea. Well done!

over 1 year ago ·
16817
9b0f7bbf22d65fa2bd23496973c0ba80

Thanks nruth! I wrote this down because I couldn't find anything like it.
Hopefully, people won't have to do the same long research I had to do!

over 1 year ago ·
17212
Da79fa81e65a88ab3c89260b7a01d5e9

How would you do this if the target element wasn't a unique element with an id, but instead if there's multiple of them with the same class name, and you want to target just that one.

Somehow you have to get the clicked element. How do you do this when you use render js? It's possible if you do a full ajax.on click event, and call $(this) within that function, but otherwise, how do you do it using render js? Cause you may want to do something on the back end in the controller or model before executing the front end part.

over 1 year ago ·
17220
9b0f7bbf22d65fa2bd23496973c0ba80

This is where you have to get creative with your javascript. Instead of using the remote: trueoption, I'd use an ajax call, withdataType: 'script'` so I can capture the Javascript behaviour returned by the controller, and/or complementing it with the logic in the success function.
With the ajax call you have greater freedom for sending parameters with the call (maybe a data-something field with the id?). If you somehow decide to send an identifier, you'd have to capture it in the controller and then use it in the response to successfully identified the correct element.

over 1 year ago ·
17652
0 je7gvrovsivfftwpgwmavtrbs pcbxvppaugvpjo5qbpz8d1luxlr1sxnd1yxirxpws sqfipvzy

i created a search page the problem is the search page has a partial which is not rendered in the start but when parameters filled and submitted then a table is rendered showing the result i am sending the parameters through AJAX but when the response comes back i am sending the response to the same controller for rendering adding an extra parameter so that it satisfies the if-else condition i placed in that controller so that the code which hit the back-end do not run ,in else block it renders the whole page search page again with form and table .i don't want to refresh the whole page,i want to render search table partial without refreshing. i tried to use respond_to |format| format.js and js.file for rendering in that div in search page but its not working ..so i am rendering whole page now. please help

over 1 year ago ·
17684
Feb6415e58c1e52463d91a065a833e4e

This is nice, thank you.

over 1 year ago ·
17707
9b0f7bbf22d65fa2bd23496973c0ba80

Hi manikantasai, please write your question in StackOverflow, where you can add the code in context (which make the analysis easier) and reach a higher audience.

over 1 year ago ·
18331
None

Thank you! I've seen a lot of tutorials, but this one is really dead simple!

over 1 year ago ·
18560
A0388ba67b5169036d7d51ba2ea17ca3

Thanks for this useful tutorial.

over 1 year ago ·
18660
None

Thanks! This was very helpful.

over 1 year ago ·
18788
4nu0pdzd normal

Beatiful post . It helped me so much. Thanks a lot !!

over 1 year ago ·
21179
None

HI, and for the URL changing, nothing?

I have problem also with Turbolinks 3. How to fix?

History.pushstate? Like in the 246 Railscast?

over 1 year ago ·
22322
14efaa5a936ab4044b42f351e0f7ef4d

Thanks man!

over 1 year ago ·
22594
0 0 ci4tnai gilx28xprn4abfi8nupvg8gknn4axfp58yuyehpqtfnlk3daqprjyuoa8zcnngncxt

just finished implementing a dynamic form with partials, based on selecting from a set of links, this guide, first time, took me 15 minutes to follow and adapt. You've got my sincere thanks, it's amazingly clear.

over 1 year ago ·
22605
None

Ruby is great but I hate using rails on the frontend because of this mess. Use angular and see how easy it is

over 1 year ago ·
22652
D5b87af3621bd16385a386558b32644e

You can use render partial: 'foo', collection: @items and then remove the manual iteration you do inside the item partial.

over 1 year ago ·
23430
None

This was super helpful, thanks so much!

over 1 year ago ·
25001
None

This is a great article. There is another method that I used with prototype helper that allows you to render the js response in the controller method without having to create a js.erb like so:
render update to |page|
page.replace_html div, partial...

Is there a possible way of doing something like this without prototype helper?

over 1 year ago ·
25151
0 7ujwfopo7i2wiu6n8uwdf2die0inseuu2elfwxxo7i7zh4urhuwhf4aoej3hieur2slw3otehmkvauo9 04vfopq5mkqa20ch04e20qwm75zxvxwummxuweslrexb2e6wyy5wtiz50g

Really nice tutorial...............Good job

over 1 year ago ·
28245

I am facing problem with is code: not really a problem but something that baffles me, something that i must have missed out, this code works fine in development mode ie. rails s but breaks when i on production environment ie, rails s -e production

http://stackoverflow.com/questions/40069866/getting-html-response-wrong-in-production-json-javascipt-response-correct

3 months ago ·