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
anddata-loading=true
attributes. -
data-remote=true
instructsjquery_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:
- Disabling the button prevents duplicate submissions.
- 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.