Halaman

Rabu, 22 Desember 2010

[shared] rails render_invalid method

purpose


provide DRY solution for rendering (or redirecting) from controller on invalid request, such as failed validation on create/update

requirements


- jquery js framework
- notifications helper from my earlier post about custom ajax helper:
http://tech.maysora.com/2010/11/rails-custom-ajax-helper-for-jquery.html

codes


application_controller.rb
# render invalid request to show error message depend on request type
# example:
#   render_invalid "Update failed."
#     ajax: show error notification with message "Update failed."
#     html: simply render text "Update failed."
#   render_invalid "Save failed.", :render => {:action => "new"}
#     ajax: show error notification with message "Save failed."
#     html: render action new with "Save failed." flash message
#   render_invalid "Save failed.", :render => {:action => "new"}, :flash => false
#     same as above but without flash for html
#   render_invalid "You can't do that.", :redirect_to => {:action => "index"}, :flash => false, :status => :unauthorized
#     same as above but redirecting to index instead of rendering new for html
#     and using unauthorized(401) status instead of not_acceptable(406)
#   render_invalid "Update failed.", :object => @user, :fields_wrapping => true
#     ajax: show error notification with addition of object's error messages and wrap error fields with div class fieldWithErrors
#     html: same as ajax minus fields wrapping
def render_invalid message="Error.", options={:flash => true}
  options[:status] ||= :not_acceptable
  message += "
#{options[:object].errors.full_messages.join(' ')}
" if options[:object].present? respond_to do |format| format.js do render(:update, :status => options[:status]) do |page| page << notification_error(message) if options[:object].present? && options[:fields_wrapping] page.rjs_clean_error_fields request page.rjs_apply_error_fields options[:object], request end end end format.html do if options[:render].present? flash.now[:error] = message if options[:flash] render options[:render].try(:merge, {:status => options[:status]}) || {:text => message, :status => options[:status]} elsif options[:redirect_to].present? flash[:error] = message if options[:flash] redirect_to options[:redirect_to] else render :text => message, :status => options[:status] end end end end

application_helper.rb (only if you use ajax error fields wrapping)
def rjs_apply_error_fields object, request
  klass_name = object.class.name.underscore
  wrapper = "
" object.errors.each do |attr, msg| attr = attr.split(".") page << "var $fields = $(), $container = $('\##{klass_name}_#{object.id || "new"}');" page << "if(!$container.length) $container = $('form[action=\\'#{request.request_uri}\\']');" page << "if(!$container.length) $container = $(document);" if attr.length > 1 object.send(attr.first).each_with_index do |x,i| page << "$fields = $fields.add('[id^=#{klass_name}_#{attr.first}_attributes][id$=#{attr.last}]:eq(#{i}),label[for^=#{klass_name}_#{attr.first}_attributes][for$=#{attr.last}]:eq(#{i})', $container)" if x.errors.on(attr.last) end else page << "$fields = $('label[for=#{klass_name}_#{attr}],\##{klass_name}_#{attr}', $container);" end page << "if(!$fields.parent('.fieldWithErrors').length)" page << "$fields.wrap('#{wrapper}')" end end def rjs_clean_error_fields request page << "$('.fieldWithErrors.rjs_#{request.request_method}_#{request.request_uri.gsub(/_=\d+($|&)/, "").parameterize} *').unwrap();" end

usage

users_controller.rb (using non ajax form)
#...

def create
  @user = User.new(params[:user])
  unless @user.save
    render_invalid "Failed to register.", :object => @user, :render => {:action => "new"}
  else
    flash[:info] = "Registration success, please check your email to activate."
    redirect_to login_path
  end
end

#...
users_controller.rb (using ajax form)
#...

def update
  @user = current_user
  unless @user.update_attributes(params[:user])
    render_invalid "Profile update failed.", :object => @user, :fields_wrapping => true
  end
end

#...
and update.js.rjs (for success behavior)
page << notification_info "Profile updated."
page.rjs_clean_error_fields request # used for cleaning error fields wrapping
#... other necessary action ...

additional tips

depending on your site style, it might be a good idea to change the div wrapper in rjs_apply_error_fields helper to span. you can also change rails standard error field wrapper for non-ajax form, see this post

Tidak ada komentar:

Posting Komentar