Sometimes we need to make post request to controller actions to do logic that we need to return back to the views. This can easily be handled with the help of jQuery and a little understanding of how rails handles request.
This may not be the best scenario for this to be applied, but I'm just giving a simple example of this use.
Let's say for example we have this code on the index page listing all the blogs:
<a href="#" id="save"> Save Changes </a>
<section id="main">
<% @blogs.each do |blog| %>
<section class="post">
<%= check_box_tag :featured, "featured", blog.featured %>
<%= label_tag :featured %>
<h2> <%= blog.title %> </h2>
<p> <%= blog.body %> </p>
</section>
<% end %>
</section>
By clicking the "save" link we want to update all the checked blogs to be featured in the backend and re-render the list ordered by their featured status.
Let's start off by setting up our javascript and get started writing this functionality.
<script>
$(function() {
$("#save").on('click', function(e) {
e.preventDefault();
// will do the fancy stuff later
});
});
</script>
We start off when the dom is loaded and ready to be acted upon. We then add an event listener to the link with an ID of "save" and prevent the default behavior from happening when that link is clicked. We will handle this ourself with a post request to our backend.
Next we need to compose the data we will be sending to the server. We will be doing this by creating an array of objects that contain the ID of the object, and whether it is featured or not. Once we have this array of objects we'll send this information to the server to apply the changes.
<a href="#" id="save"> Save Changes </a>
<section id="main">
<% @blogs.each do |blog| %>
<section class="post" data-blog-id="<%= blog.id %>">
<%= check_box_tag :featured, "featured", blog.featured %>
<%= label_tag :featured %>
<h2> <%= blog.title %> </h2>
<p> <%= blog.body %> </p>
</section>
<% end %>
</section>
<script>
$(function() {
$("#save").on('click', function(e) {
e.preventDefault();
var blog_information = [];
$('section.post').each(function(i, post){
var post_id = $(this).data('blog-id'),
featured = $(this).find("input[name='featured']").is(':checked');
blog_information.push({ post_id: post_id, featured: featured })
// will send information to the server
});
});
});
</script>
Here we add a data attribute of blog-id to the post in order to access the objects ID within our jQuery code. Then in our javascript we loop over each section with a class of post and contruct an object with the blog ID and its respected featured boolean flag. Once we have the object we push that into an array that we will send to server to be processed.
<script>
$(function() {
$("#save").on('click', function(e) {
e.preventDefault();
var blog_information = [];
$('section.post').each(function(i, post){
var post_id = $(this).data('blog-id'),
featured = $(this).find("input[name='featured']").is(':checked');
blog_information.push({ post_id: post_id, featured: featured })
$.post("/filter_featured", { blog_information: blog_information }, function(data) {
// stuff will happen once we handle the data in the server
});
});
});
});
</script>
PostRenderTutorial::Application.routes.draw do
match "/filter_featured", to: "blogs#filter_featured"
end
class BlogsController < ApplicationController
def filter_featured
end
end
As you can see here, we are making a POST request to a route that we define to direct to the filter_featured action in the blogs controller. In here we are going to update the records and re-render the layout and return the updated HTML. So lets get to it.
class BlogsController < ApplicationController
...
def filter_featured
@blogs = Blog.by_featured
params[:blog_information].each do |blog_info|
blog_info = blog_info[1]
blog = Blog.find blog_info["post_id"].to_i
blog.update_attribute(:featured, blog_info["featured"]) unless blog.featured == blog_info["featured"]
end
render "featured_blogs", layout: false
end
...
end
This method is pretty basic. We first loop over each of the paramters that were sent from our javascript POST request, and then we update the blog's featured column if it has changed. Then render "featured blogs" that will handle the new HTML.
<% @blogs.each do |blog| %>
<section class="post" data-blog-id="<%= blog.id %>">
<%= check_box_tag :featured, "featured", blog.featured %>
<%= label_tag :featured %>
<h2> <%= blog.title %> </h2>
<p> <%= blog.body %> </p>
</section>
<% end %>
class Blog < ActiveRecord::Base
attr_accessible :featured
scope :by_featured, order("featured = ?", true)
end
Here we are esentually recreating the structure we had previously. This is the HTML that will be returned from the POST request in our initial code. Let's get back there and finish this off.
<script>
$(function() {
$("#save").on('click', function(e) {
e.preventDefault();
var blog_information = [];
$('section.post').each(function(i, post){
var post_id = $(this).data('post-id'),
featured = $(this).find("input[name='featured']").is(':checked');
blog_information.push({ post_id: post_id, featured: featured })
$.post("/filter_featured", { blog_information: blog_information }, function(data) {
$("#main").html(data);
});
});
});
});
</script>
That's all it takes. One more line and we have replaced the HTML with the newly updated statuses of the blog posts. Again, this could be handled a much cleaner way, but I am simply trying to demostrate how jQuery can be used and to hopefully give people a better understanding of how to contruct your own custom actions.