DRYing up some Rails/HAML/jQuery view code
I render an alert bar as a partial at the top of the screen that gets shown to the user for success/failure/notice flash messages.
I finally have it working for most scenarios, but the code itself is repetitive for a few parts and I am not sure how I can split it up more efficiently since I am relatively new to all of this. My goal is to try not to repeat myself if possible or at least to minimize how much is repeated.
For instance, is there a way to place some of the javascript into a re-useable partial or helper function? Are there other obvious ways of making this code less repetitious?
I'm not comfortable enough yet with Rails/Ruby to understand how to improve the code, so any tips you can provide are greatly appreciated!
/ top alert area
#topAlertBar.shadow_medium.soft-hidden
- if flash.empty? && !current_user.confirmed?
- # User has yet to confirm their account
- # and there AREN'T any flash messages to show
#alertBarOffset.colordark.soft-hidden
/ placeholder for alert bar offset
:javascript
// Set the flash box content
$('#topAlertBar').html('Please confirm your account by following the instructions sent to #{current_user.email}. To resend y开发者_如何学运维our confirmation email, #{escape_javascript(link_to("click here", user_resend_confirmation_path(current_user), :class => "inlinelink", :method => :post, :remote => true))} #{escape_javascript(image_tag("ajaxOrange.gif", :class => "soft-hidden mls mbs"))}.');
// Slides down the top alert bar after page load
$('#topAlertBar, #alertBarOffset').delay(250).slideDown("fast");
// Shows & hides AJAX loading GIF when necessary
$('#topAlertBar a').click(function() {
$(document).bind('ajaxSend', function(e, request, options) {
$("#topAlertBar img").show();
});
$(document).bind('ajaxComplete', function(e, request, options) {
$(document).unbind('ajaxSend', 'ajaxComplete');
$("#topAlertBar img").hide();
});
});
- elsif !flash.empty? && !current_user.confirmed?
- # User has yet to confirm their account
- # and there ARE flash messages to show
#alertBarOffset.colordark.soft-hidden
/ placeholder for alert bar offset
- [:error, :success, :notice].each do |key|
- unless flash[key].blank?
- @msg = flash[key]
- @key = key
:javascript
// Set the flash box content
var $that = $('#topAlertBar');
$that.html('#{@msg}').addClass('#{@key}').delay(250).slideDown("fast", function() {
$(this).delay(2000).slideUp("fast", function () {
// Remove any CSS modifiers
$that.removeClass('#{@key}');
// Set the flash box content
$('#topAlertBar').html('Please confirm your account by following the instructions sent to #{current_user.email}. To resend your confirmation email, #{escape_javascript(link_to("click here", user_resend_confirmation_path(current_user), :class => "inlinelink", :method => :post, :remote => true))} #{escape_javascript(image_tag("ajaxOrange.gif", :class => "soft-hidden mls mbs"))}.');
// Slides down the top alert bar after page load
$('#topAlertBar, #alertBarOffset').slideDown("fast");
// Shows & hides AJAX loading GIF when necessary
$('#topAlertBar a').click(function() {
$(document).bind('ajaxSend', function(e, request, options) {
$("#topAlertBar img").show();
});
$(document).bind('ajaxComplete', function(e, request, options) {
$(document).unbind('ajaxSend', 'ajaxComplete');
$("#topAlertBar img").hide();
});
});
});
});
- elsif !flash.empty?
- # User is confirmed
- # and there ARE flash messages to show
- [:error, :success, :notice].each do |key|
- unless flash[key].blank?
- @msg = flash[key]
- @key = key
:javascript
// Set the flash box content
var $that = $('#topAlertBar');
$that.html('#{@msg}').addClass('#{@key}').delay(250).slideDown("fast", function() {
$(this).delay(2000).slideUp("fast");
});
Why bother with all the different states of user confirmation? Just have your application_controller set a flash alert if the user isn't confirmed.
Secondly -- move all the jquery to application.js and run it on every page -- it should slide your content down if it exists, otherwise do nothing.
Finally, grab a flash helper like the following: http://snippets.dzone.com/posts/show/6440 and then call it in your layout like
%head
%titile
=javascript_include_tag :all
=yield(:header)
%body
=display_flash
=yield
I ended up taking a different approach than what Jesse recommended, but he still helped get me thinking about ways to refactor the code. Here is the end result which is as DRY as I could get it without completely changing how I already had it implemented.
Hopefully this will help someone else who stumbles upon this question in the future.
In my ApplicationHelper (this is modified somewhat from the original question so it now works for my validation errors as well as regular flash messages)
def display_flash_messages
if !flash.empty?
[:error, :success, :notice, :warning].each do |key|
unless flash[key].blank?
@flash_key = key
if flash[key].kind_of?(Array) && flash[key].size > 1
@flash_msg = flash[key].join(' & ')
elsif flash[key].kind_of?(Array) && flash[key].size == 1
@flash_msg = flash[key].first
elsif flash[key].kind_of?(String)
@flash_msg = flash[key]
end
end
end
end
return
end
In my main layout file, I'm just doing:
%body
- if signed_in?
= render 'shared/top_alert_bar'
In the top alert bar file
= display_flash_messages
/ top alert area
#topAlertBar.shadow_medium.soft-hidden
- if !current_user.confirmed?
#alertBarOffset.colordark.soft-hidden
/ placeholder for alert bar offset
- if flash.empty? && !current_user.confirmed?
- # User has yet to confirm their account
- # and there AREN'T any flash messages to show
:javascript
#{render('shared/js/confirm_user')}
- elsif !flash.empty?
:javascript
// Set the flash box content
var $that = $('#topAlertBar');
$that.html('#{@flash_msg}').addClass('#{@flash_key}').delay(250).slideDown("fast", function() {
$(this).delay(4000).slideUp("fast", function () {
// Remove any CSS modifiers
$that.removeClass('#{@flash_key}');
#{!current_user.confirmed? ? render('shared/js/confirm_user') : ""}
});
});
In the confirm_user partial
:plain
$('#topAlertBar').html('Please confirm your account by following the instructions sent to #{current_user.email}. To resend your confirmation email, #{escape_javascript(link_to('click here', user_resend_confirmation_path(current_user), :class => 'inlinelink', :method => :post, :remote => true))}. #{escape_javascript(image_tag('ajaxOrange.gif', :class => 'soft-hidden mls mbs'))}');
$('#topAlertBar, #alertBarOffset').delay(250).slideDown('fast');
And finally, I moved this to my main js file
/* ******************************** */
/* Top Alert Bar for Flash Messages */
/* ******************************** */
// Description: Shows & hides AJAX loading GIF when necessary
$('#topAlertBar a').click(function() {
$(document).bind('ajaxSend', function(e, request, options) {
$("#topAlertBar img").show();
});
$(document).bind('ajaxComplete', function(e, request, options) {
$("#topAlertBar img").hide();
$(document).unbind('ajaxSend', 'ajaxComplete');
});
});
精彩评论