Render three different partials depending on button clicked
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.
Render partial on button click
How about this:
# main_page.html.erb
<%= link_to "FAQ", FAQ_help_path, remote: true %>
And
# faq_help.js.erb
$('#content').html("<%= escape_javascript(render :partial => 'FAQ_help')%>")
#fixed syntax
Cheers!
Is it possible to render partial onclick without link_to?
If you really want to fetch the view dynamically on clicking a link or button you have to go through by controller. AS Per MVC rule. But in your case you can try something like on loading the page render the partial and make it hidden. and on clicking a button or link just remove its hidden property through jQuery or javascript.
On click of a button display different partial views in a particular div on condition
You have to create a simple method in Controller. Then based on the condition you have to return a partial view.
Controller Method
/// <summary>
/// Choose View based on selected dropdown value.
/// </summary>
/// <param name="inputParam">Parameter.</param>
/// <returns>Partial view.</returns>
public PartialViewResult ChooseView(string inputParam)
{
//Here add your condition.
if(inputParam=="A")
{
return PartialView("_ReportHD");
}
else
{
return PartialView("_ReportSD");
}
}
Then call the AJAX function on button click. So then you can easily bind that in any div.
Note: You have to pass the selected dropdown value.
$.ajax({
cache: false,
type: "GET",
datatype:"text",
url: "/ControllerName/ChooseView?inputParam="+searchval,
success: function (result) {
$("#bindpartialviews").html(result);
}
});
Render partial view using multiple buttons
Do not use the same id more than once.
Check the documentation
The id attribute specifies a unique id for an HTML element (the value
must be unique within the HTML document).
Instead use classes:
<button type="button" class="show-partial">Button1</button>
<button type="button" class="show-partial">Button2</button>
<button type="button" class="show-partial">Button3</button>
And then assign the click listener to that class:
$('.show-partial').on('click', function() {
$('#partial').show();
});
How to update which partial is rendered after button click?
I solved this, and the following is my working solution, for how to toggle a partial for a button when the button is clicked.
My scenario is a user events application, where the user can search for events matching criteria like dates, title, venue, etc. I have a event search results page where users can click an "Add to favorites" button. When they do that the button should be replaced with a "Remove from favorites" button, in case they changed their mind and don't want that favorite.
My solution uses a respond_to with format.js in the controller action which allows user to stay on that search results page, with no page refresh. Screen shot and only relevant code below.
Hope this helps someone else. Thanks all for your suggestions!
---------- portion of search results item code -----------
<div id="add_remove_favorite">
<% if (signed_in? && current_user && current_user.id != user_event.user_id) %>
<%= render partial: "shared/add_remove_favorite",
locals: { user_event: user_event } %>
<% end %>
</div>
---- partial to decide if "Add to favorites" or "Remove from favorites" should display ----
<% if current_user.following?(user_event) %>
<%= render partial: 'shared/remove_favorite',
locals: { user_event: user_event } %>
<% else %>
<%= render partial: 'shared/add_favorite',
locals: { user_event: user_event } %>
<% end %>
------------ partial for "Add to favorites" button ----------------
<%= form_for(current_user.favorites.build(followed_event_id: user_event.id),
html: { id: "event_number_#{user_event.id}" }, remote: true) do |f| %>
<div class="hidden"><%= f.hidden_field :followed_event_id %></div>
<%= f.submit "Add to favorites %>
<% end %>
------------ partial for "Remove from favorites" button ------------
<%= form_for(current_user.favorites.find_by_followed_event_id(user_event),
html: { id: "event_number_#{user_event.id}", method: :delete }, remote: true) do |f| %>
<%= f.submit "Remove from favorites %>
<% end %>
-------- FavoritesController with create and destroy actions ---------
class FavoritesController < ApplicationController
before_filter :signed_in_user
def create
@user_event = UserEvent.find(params[:favorite][:followed_event_id])
current_user.follow!(@user_event)
respond_to do |format|
format.html { redirect_to user_path(current_user) }
format.js
end
end
def destroy
@user_event = Favorite.find(params[:id]).followed_event
current_user.unfollow!(@user_event)
respond_to do |format|
format.html { redirect_to user_path(current_user) }
format.js
end
end
end
------------- create.js.erb -----------
<% if @current_user.following?(@user_event) %>
$("#event_number_<%= @user_event.id %>").replaceWith('<%= escape_javascript(render(
partial: 'shared/remove_favorite', locals: { user_event: @user_event })) %>');
<% end %>
------------- destroy.js.erb ----------
<% if !@current_user.following?(@user_event) %>
$("#event_number_<%= @user_event.id %>").replaceWith('<%= escape_javascript(render(
partial: 'shared/add_favorite', locals: { user_event: @user_event })) %>');
<% end %>
render partial on click
You can do that with javascript like:
<%= link_to "Delete", delete_content_path, :remote => true %>
The action in your corresponding controller then will be this:
My Controller:
def delete_content
respond_to do |format|
format.js
end
end
Then you can create the delete_content.js.erb
inside your correct directory of the link and there you put the following code:
delete_content.js.erb
$('#div_id').html("<%= render :partial => 'my_partial' %>");
Then in your view:
delete_content.html.erb
<div id = "div_id">
#this div is html div that will render your partial
</div>
Don't forget to put your partial _my_partial.html.erb in the same folder.
Related Topics
Use of Caret Symbol (^) in Ruby
Check If an Array Is Subset of Another Array in Ruby
Problems Setting a Custom Primary Key in a Rails 4 Migration
How to Use Controller Specific Stylesheets in Rails 3.2.1
How to Communicate with Threads in Ruby
Is There an Equivalent Null Prevention on Chained Attributes of Groovy in Ruby
How to Convert a String to Lower or Upper Case in Ruby
Is It Ok to Use 'Any' to Check If an Array Is Not Empty
Ruby Inject with Initial Being a Hash
What Does the Equal ('=') Symbol Do When Put After the Method Name in a Method Definition
How to Get a Particular Line from a File
Rspec Failing Error: Expected False to Respond to 'False'
Yaml Indentation for Array in Hash
Railstutorial - Chapter 8.4.3 - Test Database Not Clearing After Adding User in Integration Test
Create a Daemon with Double-Fork in Ruby
Why Does Array.Each Behavior Depend on Array.New Syntax