Progress Indicator

If you ever wondered how this rotating thing works, you should definitely check this post. I will describe how to create AJAX-based progress indicator (or a progress bar) with Rails application.

Basics

No rocket science is involved here:

  • Indicator itself is an animated gif image

  • It is typically placed within hidden div container with position fixed as a background

  • JQuery (or pure JavaScript) is used to show and hide this div at appropriate times

  • Rails send some JavaScript code as a response to AJAX-based form action (<form ... data-remote="true">)

Requirements

Let’s assume that we have a simple Rails application where we want to manage users: list them, block them, and assign admin rights to them.

Here is the data model of users table:

Users Table

And here is how it looks:

Users Table in Rails

What we want to do, though, is to update is_blocked and is_admin fields using AJAX.

Algorithm

This is how we assume this would work:

  • User clicks on a checkbox

  • Progress indicator is displayed

  • After the field in the database has successfully updated, progress indicator disappears

  • Error message is shown in case of problems with the previous step (not covered in this chapter)

Step I - HTML & CSS

First of all, we add appropriate div to app/views/layouts/application.html.erb file:

<!DOCTYPE html>
<html>
<head>

...

</head>
<body>
  <div class="ajax-progress"></div>

   ...

</body>
</html>

Then we have to get gif image itself. I can recommend a very good site, where you can generate whatever progress indicator you want, with different shapes and colors.

The most difficult part here is to find the indicator to your heart’s consent. So when you finally choose it, put it in app/assets/images/ folder.

In order to get our div container working properly, we have to define its CSS. The Rails-way is to edit app/assets/stylesheets/custom.css.scss file:

div.ajax-progress {
  position: fixed;
  display: none;
  width: 50px;
  height: 50px;
  top: 50%;
  left: 50%;
  background-image: url('ajax-loader.gif');
  background-position: center;
  background-repeat: no-repeat;
  background-color: white;
  border: 1px solid green;
  -moz-border-radius:    10px;
  -webkit-border-radius: 10px;
  border-radius:         10px;
}

Step II - JavaScript

So now we have our progress indicator image and container, which is hidden by default. Now we have to add some programming logic behind all this stuff.

First of all, we define AJAX-based form in our users view. I’m using app/views/users/index.html.erb file with <%= render @users %>, so we should change app/views/users/_user.html.erb file, where all the logic is defined:

<tr>
  <%= form_for(user, :remote => true) do |f| %>
    <td><%= user.email %></td>

    ...

    <td><%= f.check_box :is_admin, :class => "submit" %></td>
    <td><%= f.check_box :is_blocked, :class => "submit" %></td>
    <% end %>
  <% end %>
</tr>

As I mentioned before, we want our indicator displayed after clicking on a checkbox. So we need to handle this event somehow. For this purpose we would use a file called app/assets/javascripts/ajax-submit.js:

// AJAX Form Submit
  $(".submit").on("change",
    function(event) {

      // Show Progress Indicator
      $(".ajax-progress").show();

      ...

      $(this).closest("tr").find("form").submit();

    });
// END

And, of course, we should include it in our app/views/users/index.html.erb view:

<% provide(:title, 'Users') %>
<h2>Users</h2>
<br>

<table class="table table-striped">
  <thead>

    ...

  </thead>
  <tbody>
    <%= render @users %>
  </tbody>
</table>

<%= javascript_include_tag "ajax-submit" %>

So now we have indicator displayed after clicking a checkbox. The next thing is to hide it after our action is completed.

We create AJAX response template app/views/shared/ajax-progress.js.erb (I want to use it in several other places, such as collectors list, so I create it shared):

$(".ajax-progress").hide();

Step III - Rails

The final piece, that glues all the logic together, is an update action in our app/controllers/users_controller.rb file:

class UsersController < ApplicationController

  ...

  def update
    @user = User.find(params[:id])
    @user.attributes = params[:user]

    ...

    # Error handling is not covered in this chapter
    @user.save(:validate => false)

    respond_to do |format|
      format.html { redirect_to users_path }
      format.js { render 'shared/ajax-progress' }
    end
  end

  ...

end

Conclusion

GetMedia Progress

We created a basic progress bar functionality, using Rails, JQuery, and CSS. We haven’t touched error handling yet, which is very interesting and challenging itself in this AJAX-based environment.

Luckily, we have tools, such as Twitter Bootstrap components, which are going to help us a lot.