Skip to content

Instantly share code, notes, and snippets.

@gogogarrett
Created November 19, 2012 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gogogarrett/8770b7ff032a0ed89bb9 to your computer and use it in GitHub Desktop.
Save gogogarrett/8770b7ff032a0ed89bb9 to your computer and use it in GitHub Desktop.

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>

routes

PostRenderTutorial::Application.routes.draw do
  match "/filter_featured", to: "blogs#filter_featured"
end

controller

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.

featured_blogs.html.erb

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

Blog.rb

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment