remote_function and :with or :submit, an update on previous post about graceful Ajax fallback

Posted by Tim Connor Tue, 07 Nov 2006 17:20:00 GMT

So the last post was a little more oversimplified than I realized. When I went to fix the bug in my implementation, I discovered I was leaving some important stuff out, like actually submitting the value of the form variable you want. :( As such, maybe it is complicated enough to use a nice concrete example.

There are two ways you can can get post the values to your remote_function (post, so discounting just putting it in the url). You can use

:with


<%= submit_tag 'Save', :name => 'save', :id => 'save' %>
<%= submit_tag 'Update Preview',
:name => 'preview',
:onclick => remote_function(
:url => {:action => :preview},
#:with => "'name_of_form_field'=value_you_want_and_javascript_is_legal" 
:with => "'patch[editing_rdoc]='+$('patch_modified_rdoc_source').value", 
:before => "$('preview_target').innerHTML='..loading..'",
:failure => "$('preview_target').innerHTML='Ajax error!'",
:update => 'preview_target'
) + ';return false;'
%> 

or :submit

<%= submit_tag 'Save', :name => 'save', :id => 'save' %>
<%= submit_tag 'Update Preview',
:name => 'preview',
:onclick => remote_function(
:url => {:action => :preview, :_method => :post},
:submit => "id_of_my_form",
:before => "$('preview_target').innerHTML='..loading..'",
:failure => "$('preview_target').innerHTML='Ajax error!'",
:update => 'preview_target'
) + ';return false;'
%> 
Note that :submit required adding the _method to my form url – I am not quite sure why it didn’t just use the one already in the form itself. Now a simplified version of my controller

#Called by the xhr
def preview
  @source = params[:patch][:editing_rdoc] unless params[:patch].nil? or params[:patch][:editing_rdoc].nil?
  render :layout => false
end

#called with fallback from .js being off
def create
  @patch = Patch.new params[:patch]
  respond_to do |format|
    #this is the heart of the matter - if preview wasn't pressed, just output the template again, with @patch values updated, but not saved to db.
    if !params[:preview] and @patch.save    
      format.html { redirect_to patch_url(@patch)  }
      format.xml do
        headers["Location"] = patch_url(@patch)
        render :nothing => true, :status => "201 Created" 
      end
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @patch.errors.to_xml }
    end
  end
end
and of course, don’t forget your very complicated preview template (simple_markup is a project specific helper, always remember your “h” or whatever you are using to escape)

<%= simple_markup @source -%>

Maintaining degradibility with two submit buttons instead of submit_to_remote, for preview 5

Posted by Tim Connor Mon, 06 Nov 2006 23:23:00 GMT

Now sometimes you want multiple possible branches of results from one form, like “save” and “preview”. Yes, you could change the action of the form with some Javascript, based on a button click, but that’s a little clunky, in my opinion, and very non-degradable. Well good old fashioned web 1.0 sorts will be familiar with just using two submit buttons and checking on those, and of course, that’s nice and easy, in rails: http://www.railsdiary.com/diary/two_submit_buttons.

As that linked to post points out, you can be smart and just check for the existence of the name of the input, and not have to worry about matching the value of the button (and thus be tied to the display text of the submit), such as

view:
<%= submit_tag "Save My Stuff", :name=>"save" %> 
<%= submit_tag "Show me the Preview", :name=>"preview" %>

controller:
if @params['save']" 
else
end

Ajaxy 2.0 previews are all the buzz, so you rip out one of those submits and replace it with a submit_to_remote to a separate action. Well, the problem with that is submit_to_remote doesn’t degrade due to using a button instead of a submit. So for now you just attach an onclick with a remote_function callback to your preview submit, instead and point it at your preview action. Remembering of course to cancel the form submission, so you don’t get a non-Ajax submission overriding your remote call. And keep your checks for the ‘preview’ param in the default action of the form, for when people hit the button without Ajax.

To be really clean about it you should use the UJS plugin for Rails and attach your Ajaxing as a behavior. Then wallah, fully functional degraded form and clean XHTML, with functionality extended via external javascript, intead of spaghetti HTML built around javascript. And at this point it’s easy to add an auto-updating preview, with an observer on the text-field.

Of course, I injected at least one bug in the most recent usage of this simple technique, somehow, and am waiting for court3nay to finish some changes so I can get in there and fix it. But the time before (Scribbish theme fix) seemed to work fine.

Fix for Typo Scribbish theme v2 - aka disabling a submit button in IE 2

Posted by Tim Connor Thu, 26 Oct 2006 17:11:00 GMT

I looked into it a little more this morning, and came up with how to still get the submit button disabling, to avoid double-posts, without having to rework anything substantially.


<%= submit_tag 'Submit', :onclick => "$('commentform').onsubmit();this.disabled=true;Element.hide('preview');return false;"%>

You first call the form’s onsubmit, since IE won’t call it if you submit the form via JavaScript, then you prevent browsers that don’t cancel the form when you disable the submit button from double-submitting, by returning false at the end.

This should still work with javascript off, if you have the “Allow non-Ajax comments” setting turned on.