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 grounds for this example.
Let's say for example we have this code:
<a href="#" id="save"> Save Changes </a>
<section id="main">
<%= @blogs.each do |blog| %>
<section class="post">
<%= check_box_tag :feature %>
<%= label_tag :feature %>
<h2> <%= blog.title %> </h2>
<p> <%= blog.body %> </p>
</section>
<% end %>
</section>
We want to update with a check box if we want to mark it to be featured. 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 featured.
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, then send this information to the server.
<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 %>
<%= 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 = $(post).data('post-id'),
featured = $(post).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 post and push to an object containing the post id and the featured boolean flag to the blog_information array that we will send to the server in a moment.
...
<script>
$(function() {
$("#save").on('click', function(e) {
e.preventDefault();
var blog_information = [];
$('section.post').each(function(i, post){
var post_id = $(post).data('post-id'),
featured = $(post).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 we configured to direct to the filter_featured method 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.
class BlogsController < ApplicationController
def filter_featured
params[:blog_information].each do |blog_info|
blog = Blog.find blog_info["post_id"]
blog.update_attribute(:featured, blog_info["featured"]) unless blog.feature == blog_info["featured"]
end
render "featured_blogs", layout: false
end
end
Here we loop over each of the objects we sent to the controller, find the blog and update the featured column if it has changed. Then we render a partial "featured_blogs" and here is where the magic is going to happen.
<%= @blogs.by_featured.each do |blog| %>
<section class="post" data-blog-id="<%= blog.id %>">
<%= check_box_tag :featured %>
<%= label_tag :featured %>
<h2> <%= blog.title %> </h2>
<p> <%= blog.body %> </p>
</section>
<% end %>
class Blog < ActiveRecord::Base
attr_accessible :featured, :title, :body
scope :by_featured, .order("featured = ?", true)
end
Here we are esentually recreating the structure we had previously with 1 modification, a scope we created to order by the featured status. 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 = $(post).data('post-id'),
featured = $(post).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 order of the blog post. Again, this could be handled a much cleaner way with rails and a form, but I am simply trying to demostrate how this can be handled with jQuery and hopefully help people get a better look into how rails can be used in conjuction.