# Behave like +link_to_remote+, but shows a spinner while the request is processing.
# link_to_remote_with_spinner 'Hi', :url => hi_path
# You can specify the spinner and the container to hide
# link_to_remote_with_spinner 'Hi', :url => hi_path, :container_id => 'container', :spinner => 'spinner'
def link_to_remote_with_spinner(title, options)
element_id = options.delete(:id) || ('link_to_' + title.underscore.tr(' ', '_'))
container_id = options.delete(:container_id) || element_id
returning '' do |out|
unless spinner = options.delete(:spinner)
spinner = "#{element_id}_spinner"
out << image_tag('spinner.gif', :id => spinner, :style => 'display:none')
end
options[:complete] = "$('#{spinner}').hide(); " + (options[:complete] || "$('#{container_id}').show()")
out << link_to_remote(title, { :loading => "$('#{container_id}').hide(); $('#{spinner}').show()" }.merge(options),
{ :id => element_id })
end
end
Refactorings
No refactoring yet !
macournoyer
September 21, 2007, September 21, 2007 07:42, permalink
With the above code you could already specify the onComplete callback overriding the container hiding thing, I just added the same thing for the loading callback and now you got more freedom then a jobless freelancer on crack.
def link_to_remote_with_spinner(title, options)
element_id = options.delete(:id) || ('link_to_' + title.underscore.tr(' ', '_'))
container_id = options.delete(:container_id) || element_id
returning '' do |out|
unless spinner = options.delete(:spinner)
spinner = "#{element_id}_spinner"
out << image_tag('spinner.gif', :id => spinner, :style => 'display:none')
end
options[:complete] = "$('#{spinner}').hide(); " + (options[:complete] || "$('#{container_id}').show()")
options[:loading] = "$('#{spinner}').show(); " + (options[:loading] || "$('#{container_id}').hide()")
out << link_to_remote(title, options, { :id => element_id })
end
end
# Now if you specify complete and loading you can do wathever you want:
link_to_remote_with_spinner 'Grey out text', :url => ouch_path, :id => 'my_link',
:loading => toggle = "$('my_link').toggleClassName('disabled')",
:complete => toggle
link_to_remote_with_spinner 'Dont click!', :url => ouch_path,
:loading => "$('notice').update('I told you not to click')",
:complete => "$('notice').update('Ok now! Look what you did, stupid!')"
Andrew Kaspick
September 27, 2007, September 27, 2007 23:51, permalink
Use my plugin at http://agilewebdevelopment.com/plugins/remote_helpers
See the readme for info on more advanced usage... or email me.
<%= link_to_remote ... %> <%= indicator %>
Andrew Kaspick
September 28, 2007, September 28, 2007 01:33, permalink
Using my plugin remote_helpers from http://agilewebdevelopment.com/plugins/remote_helpers, all of the spinner functionality is taken care of. It also works with any method that makes a remote function call.
See the plugin README in the plugin for more detailed usage.
The code below is the most basic usage. The 'indicator' method contains the spinner image and that's all there is to it... the spinner automatically gets displayed and hidden.
<%= link_to_remote 'Text', :url => your_url %> <%= indicator %>
Ryan
October 8, 2007, October 08, 2007 23:04, permalink
Anymore, I always use EventSelectors.js to handle all toggling of the "loading" image. I usually keep the loading image in a nice place in the layout (often absolutely positioned) and show activity that way.
References:
1) <a href="http://rpheath.com/posts/2007/03/17/add_a_loading_image_for_all_ajax_requests">loading image for all ajax requests</a>
2) <a href="http://rpheath.com/posts/2007/06/01/rails-plugin-for-handling-ajax-spinners">Rails plugin to show loading image</a>
# include the eventselectors.js library in your layout
# put this at the very bottom:
Ajax.Responders.register({
onComplete: function() { Element.hide('loading'); EventSelectors.assign(Rules);},
onLoading: function() { Element.show('loading');}
})
We use this utility internally. I had a use case today where I don't want to $('#{container_id}').hide(); rather I'd like to grey it out and stop it from receiving events. Is there a more generic way to do all this?