Render Three Different Partials Depending on Button Clicked

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: truein the link_to helper. This adds a data-remote=truein 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 forbooks`.

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!

screenshot

---------- 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



Leave a reply



Submit