Published by Dan Cunning on Jul 14, 2016

Loading Buttons

Converting action buttons into loading buttons is a common way for AJAX applications indicate that it’s waiting on the server to verify an action and respond.

Filed under Features

Introduction

Loading buttons are a common user-experience concept used to indicate the state between starting and completing an action. They are most useful when server-side logic decides what happens next, such as displaying validation errors or a success message.

Single page applications may bypass loading buttons to appear more responsive since they use client-side logic, but loading buttons still play a large role in the standard MVC Rails application.

represents an action for the user to perform.

indicates the action in processing via a loading icon and disabled button.

Demo

Click the buttons below for a live demonstration.

This webpage is not hosted by Rails, so the above UX is just a simulation.
A "Log out" button would normally redirect you to the sign-in page.

How it works

  • A Rails helper method renders a button with data-remote=true and data-loading=true attributes.
  • data-remote=true instructs jquery_ujs.js to begin an ajax request.
  • data-loading=true instructs a jQuery listener to disable the button and insert a spinner.
  • Your Rails application determines what happens after the request finishes:
    • Reset the button's state
    • Redirect to another page

The Code

# config/routes.rb
resources :sessions, only: %w(new destroy)
# controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def destroy
    reset_session
  end
end
# helpers/sessions_helper.rb
module SessionsHelper
  def link_to_log_out
    link_to_with_icon('sign-out', 'Log out', url, {
      method: 'DELETE',
      remote: true,
      class: 'btn btn-primary',
      data: {loading: true}
    })
  end
end
# helpers/layout_helper.rb
module LayoutHelper
  def icon_tag(icon)
    content_tag(:span, nil, class: "fa fa-#{icon}")
  end

  def link_to_with_icon(icon, title, url, options = {})
    title_with_icon = icon_tag(icon) << ' '.html_safe << h(title)
    link_to(title_with_icon, url, options)
  end
end
# Gemfile
gem 'jquery-rails'
// assets/javascript/application.js
//
// jquery_ujs allows us to use 'data-remote',
// 'data-type', and 'data-method' attributes
//
//= require jquery
//= require jquery_ujs
//= require loading
# assets/javascripts/loading.coffee
$ ->
  # Insert a spinner and disable the button
  $(document).on('click', 'a[data-loading]', (e) ->
    $(this).attr('disabled', 'disabled')
    $(this).find('.fa:first').attr('class', 'fa-refresh fa-spin')
<%# views/layouts/application.html.erb %>
<%= link_to_log_out %>
/* views/sessions/destroy.js.erb */
Turbolinks.visit("<%= new_session_path %>");

Wrap-Up

The Rails code is rather boring, but two important user-experience concepts occur on the frontend:

  1. Disabling the button prevents duplicate submissions.
  2. Using a loading spinner lets the user know we started performing the action.

For further reading, my AJAX Toggle Buttons article builds on these concepts to implement an on/off feature for buttons.