Ok, before getting into the solution, lets understand the request-response
cycle.
When you search for http://stackoverflow.com, what happens is you're sending a request from the client
(your browser) to the StackOverflow(SO) server
. The client and server communicate through HTTP
protocol and if the user's requesting for something that the server knows, it serves(sends) the response(html, css, js files). The browser knows how to display the html content received from the server. Every browser has its own stylesheet(user-agent-stylesheet
) and it also applies the styles in the css files linked in the html page sent back from the server. Note that this all happens synchronously and while the server
is processing the client's request, the browser tab is inactive
as its waiting for the server's response. The same process happens when you click on a link. It creates a new request to the server.
The response from the server can be HTML
, JSON
, XML
etc. As you might have noticed, the synchronous
communication is not something we always want.
If we make a new synchronous
request, the browser fetches HTML
, CSS
and JS
and image
files all over again(lets not get into caching). And we don't want to update the whole page for every request.
Often, only parts of the page are updated after a request and it provides a good user experience.
This is where Javascript is good at. It has the power to make requests to the server asynchronously(the web page doesn't reload) and also update parts of the page using something called AJAX
(Asynchronous Javascript XML).
A typical AJAX request goes like this. You make a request to the server but this time asynchronously and the server responds with XML
rather than HTML
and Javascript
parses the XML
document updates the part of the page. Eventhough its called AJAX, now-a-days, JSON is used for exchanging information across services.
So, to make a AJAX request, we need a link which when clicked sends a XMLHttpRequest
(asynchronous request) and the server should respond with JSON
or XML
or and then the script should parse the response and update the DOM
(Document Object Model). Making an AJAX request in Vanilla JS
(plain javascript) is complex and people normally use Jquery
's ajax
method to issue an AJAX request(less lines of code). See http://api.jquery.com/jquery.ajax/ for more information.
But in rails
, its even easier. We can make an AJAX request using UJS
(Unobtrusive Javascript). Lets see it in action.
To make a link send an AJAX request, you need to set remote: true
in the link_to
helper. This adds a data-remote=true
in the generated HTML.
For example the following erb
<%= link_to "All books", books_path, remote: true %>
generates the html
<a data-remote="true" href="/books">All books</a>
Ok. Now we're all set to make an AJAX
request. Modify your code as
<div style="margin-top:50px;" class="wrapper">
<div class="optionscontainer btn-group btn-group-justified">
<%= link_to posts_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-book optionseach" aria-hidden="true"></i>All posts
<% end %>
<%= link_to stories_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-rss optionseach" aria-hidden="true"></i>All stories
<% end %>
<%= link_to books_path, class:"options btn btn-primary", remote: true do %>
<i class="fa fa-users optionseach" aria-hidden="true"></i>All books
<% end %>
</div>
<div id="content">
<!-- The content goes here -->
</div>
I assume you have the controllers, models and views setup. Also Execute rake routes
in the terminal to see the existing routes of your application. You should see the following(Order isn't important)
Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
stories GET /stories(.:format) stories#index
books GET /books(.:format) books#index
Note: format
here corresponds to the returned format which can be html
, js
, xml
or json
.
posts_path
in one of the url_helper
which points to posts#index
, meaning whenever a request is made to the server in a rails
application, it first reaches the router and is dispatched to the corresponding controller
action specified in routes.rb
In this case, if we make a request to http://localhost:3000/books
, the request is sent to the books#index action. In the action, you can fetch the data from the database
and send the response to the client.
Since we are interested in AJAX and we have specified remote:true
, rails will expect a JS
response to be returned to the client(ie. a script
which is responsible for rendering content dynamically).
I will explain how to deal with the AJAX request for BooksController
and you can apply the same idea for other controllers.(posts
and stories
).
class BooksController < ApplicationController
def index
@books = Book.all
respond_to do |format|
format.html #looks for views/books/index.html.erb
format.js #looks for views/books/index.js.erb
end
end
#other actions
end
All we're doing here is telling the controller to render index.js.erb
if the client requests for a JS response or to render index.html.erb
in case of HTML response. How does rails knows to render index.html.erb
or index.js.erb
when we didn't specify the file to render? Thats what rails is popular for.Rails follows Convention Over Configuration.
Actually, the controller
infers the template to render from the action
name.
The next step is to make use of @books
to update the #content
div. Before adding the code to render all the books, we need a template to render right? Thats where partials come in. A partial is a reusable view' and a partial in rails is prefixed with '_'. For example:
_books.html.erbis a partial for
books`.
Create a partial app/views/books/_books.html.erb
<% @books.each do |book| %>
<div class="book">
#Display the fields
</div>
<% end %>
Now create app/views/books/index.js.erb
and add the following:
$("#content").html("<%= j (render 'books') %>");
This one-liner will render the partial _books.html.erb
into the #content
div. Wait. How does it work? Lets break it into pieces.
Anything inside <%= %>
is ruby code. Its executed and the value is replaced in place of <%= %>
. The erb
templating engline allows you to write ruby
code inside javascript
. So, what does this do?
<%= j (render 'books') %>
It will render the books/_books.html.erb
, inferred from the parameter to render
. It returns the html generated by _books.html.erb
.
What does j
do? Its actually an alias of escape_javascript
method. It is used to escape the content returned from the partial _books.html.erb
.
Explaining the reason for escaping
html will make this answer even longer. I strongly recommend you to read kikito's answer(the 3rd one) in this SO thread.
So, we are passing html from the partial as a string(note the quotes around <%= %>
) to html
method which is added inside the #content
div. Thats it!
I recommend you to inspect the server logs and explore the Network
tab in Developer tools to get a deep understanding of how AJAX works.
Do the same for the other controllers(PostsController
and StoriesController
).
Hope this helps.