Rails 3 disable_with Does Not Work with Ajax Remote Form_for

It appears that the :disable_with option on the submit_tag form helper method does not behave as expected with remote forms. I’m not sure if this is a bug or not. But the fix is pretty straight forward, but perhaps a little difficult to trouble shoot. Using submit_tag inside a remote form

<%= submit_tag "Submit", :disable_with => "Submitting...." %>

Will not work.

You have to edit your public/javascripts/rails.js file around line #167

and change

document.on("ajax:after", "form", function(event, element) {

to

document.on("ajax:complete", "form", function(event, element) {

Then things will behave as expected. This assumes you are using Prototype (rather than jQuery).

Execute Javascript When Using Link_to_function To Include a Partial in Rails

If you use the link_to_function to replace content in a div with content from a partial, any javascript that you include in the partial will not be executed. It is instead included, but will do nothing. Obviously, this isn’t the desired behavior. Why would we be taking the trouble to write the javascript in the partial? There is an easy fix. Instead of writing out the script tags, instead use the javascript_tag  method to wrap whatever javascript you would like executed when the partial is loaded.

<%=link_to_function "say hello" {|p| p.replace_html "container", :partial => "say_hello" }%>

And now in my say_hello.erb file

<% javascript_tag do %>
  alert("Hello World");
<% end %>

Onchange Event Fired from Select Field in Rails Form

In the view there is a regular Rails form and a javascript function that will be triggered when the country select field is changed. The javascript function will make an ajax request to the country_select url with the country code passed as the id variable, e.g., /country_code/us for the United States. I’m also using the Carmen plugin for this example which will provide a list of countries and their respective states/provinces. Not all countries are full supported. More information on Carmen can be found at http://autonomousmachine.com/2009/4/1/carmen-a-rails-plugin-for-geographic-names-and-abbreviations and http://github.com/jim/carmen

<%form_for(@model) do |f| %>
<script type="text/javascript" charset="utf-8">
  function change_state_select(state_code)
 {
    new Ajax.Request('/country_select/'+state_code,
    {
      method: 'get',
      onSuccess: function(transport) {
        $('state_select').replace(transport.responseText);
      }
    });
  }
 </script>
 <%= f.select :country,
   Carmen::COUNTRIES,
   {},
   { :onchange => "change_state_select(this.options[this.selectedIndex].value);" }
%>
<div id='state_select'></div>

***Note the : onchange should really be one word but an emoticon shows up otherwise :onchange :(***
Since not all countries are supported I need to execute some conditional logic in the action country_select. If the country is supported I’ll return a snippet of html containing a select field that my form will use. If the country is not supported I’ll return a text field so that the user can write in their state/province.

class CountrySelectController < ApplicationController
  def country_selecet
      begin
         @states = Carmen::states(params[:id])
      rescue
         @states = nil
      end
      render :partial => "country_select/states"
  end
end

In the final partial that is rendered there is either a select field or a text field

<div id="state_select">
  <% if @states.nil? %>
    <%= text_field_tag :model, :state %>
  <% else %>
    <%= select :model, :state, @states%>
  <% end %>
</div>

Accessing Links in Nested TD Cells with Prototype

There must be a better way to do the following with PrototypeJS. I want to loop over nested links inside of table td cells and apply a class to the row that the link is in when the link is clicked. If a user clicks on another link the class will be removed and applied to the current table row.

The code below works but I think it seems hackish. Any thoughts on improving this code?

Here is the link to the pastie http://pastie.org/703316

# This is the table with haml markup
%table
  %tr
    %td
       info
    %td
      = link_to "be clicked", remote_request_goes_here_path

And the Js

	$$("td.filter_link > a").each(function(element){
		Event.observe(element, "click", function(event){
			$$("td.filter_link").each(function(el){
				if(el != this){
					el.up().removeClassName("turnwhite");
				}
			});
                       // apply the turnwhite class to the table row
			this.up().up().addClassName("turnwhite");
		});
	});

Double Click Event Using Prototype Javascript Framework

Super simple to get double click “desktop” like functionality out of Prototype! For some reason, googling it doesn’t yield many useful results. http://www.google.com/#hl=en&q=double+click+in+prototype+javascript&aq=f&oq=&aqi=&fp=peEfEjG9pWY :(

Anyway, here is the code

	document.observe('dblclick', function(){
		alert("Hello World");
	});

Rails Prototype JS and TinyMCE Autosave

TinyMCE is a nice little WYSIWYG for text processing online. It uses iFrames and Javascript callbacks to manipulate textarea form fields. Using it with Rails can be somewhat problematic if you want to set up an observer on a field that TinyMCE is managing. The reason is that TinyMCE uses an iFrame and a callback to update the textarea before the form is submitted.

More detailed information on the TinyMCE specifics can be found at this site <a href=”http://www.crossedconnections.org/w/?p=88″>

In Rails, there is a nifty helper function called “observe_field” that will generate Javascript which listens for changes on form fields. However, because TinyMCE only updates the field after the form is submitted the contents of the textarea are not updated when you type in it. Therefore, your observe_field function is listening but doesn’t see anything new. The iFrame is a kind of buffer that will write to the textarea later.

A simple solution to this problem is to call the function that writes to the textarea. This will update the textarea and the observer_field function will notice the changes. You can use prototype “PeriodicalExecuter” to trigger a function that will contain the TinyMCE callback function. Below is the code.

script type="text/javascript" charset="utf-8"
  function triggerTinyMCE(){
    tinyMCE.triggerSave(true, true);
  }
  new PeriodicalExecuter(triggerTinyMCE, 30);
/script

<%= observe_field( :post_body,
        :frequency => 1,
        :update => :update_status,
        :url => { :action => :update_body } ) %>

However, the only problem with this approach is that you’re kind of running two functions that do the same thing. Instead it would make more sense to get rid of the observe_field function altogether and have the PeriodicalExecuter trigger a function that makes the ajax request at the same time. This would look something like

script type="text/javascript" charset="utf-8"
  function triggerTinyMCE(){
    tinyMCE.triggerSave(true, true);
    var content = escape($F('post_body'));
    var url = "/posts/update_body";
    var params = "post_body="+content;

    var aRequest = new Ajax.Request(url,
    {
      method: 'post',
      parameters: params,
      onSuccess: function(data){
        $("update_status").innerHTML = data.responseText;
      }
   });
}
new PeriodicalExecuter(triggerTinyMCE, 10);
/script

Using jQuery and Prototype Javascript Together with jQuery.noConflict();

To use the jQuery javascript framework in a Rails application, that also uses the Prototype framework for the same application, you’ll need to reassign the $() function for jQuery to another variable. This is very simple to do. Just make sure to include the jQuery library after you load your prototype defaults.

Here is the javascript to alias the $() function

script type="text/javascript"
  var $_ = jQuery.noConflict();
  $_(document).ready(function(){
  $_("a").click(function(){
  //... do something
  });
});
/script

Using Prototype to Access Form Data

Prototype has a powerful API for accessing and manipulating the Document Object Model, A.K.A the DOM. The following code will let you interact with a simple web form.

Suppose we have a form that contains hidden/or locked inputs and they need to be updated dynamically. If the user changes a select field or if a checkbox is de/selected, other values in the form need updating. This could be required when products have options and associated price points. If you want to offer the user the option to switch options without refreshing or navigation this is a simple and effective approach.

… Sample form…

&lt;input type="hidden" name="option_plan_id" id="option_plan_id" value="1"&gt;&lt;br/&gt;
&lt;input type="hidden" name="amount" value="150" id="amount"&gt;

&lt;select name="select-plan" id="select-plan"&gt;
&lt;option value="1, 150"&gt;
USD 150 - 1 person
&lt;/option&gt;
&lt;option value="2, 200"&gt;
USD 200 - 2 people
&lt;/option&gt;
&lt;/select&gt;

In this example I want to update the values on the amount and option_plan_id input fields. The first thing I need to make sure of is that the page has fully loaded. I’ll make sure that my code executes in this context by placing it inside the document.observe method. I’ll then set up an observer on my select field and if it changes, make assignments. Now, in this example, I just saved the plan id and amount information as a string. In my javascript code I split apart the string and then make the assignments. There are other, probably better, ways of doing this. For instance you could store serialized data/json, and then work with it inside your event. This seems simple enough, however, you’ll have to pay attention in the future, if your storage format changes! Here is the sample Prototype javascript code

&lt;script type="text/javascript" charset="utf-8"&gt;
document.observe("dom:loaded", function(){ //when dom is fully loaded do -
//update fields when option is selected
Event.observe('select-plan', 'change', function(event){
var data = this.getValue();
var plan = data.split(",").first(); //breaks apart by comma
var amount = data.split(",").last();
$('plan_option_id').setAttribute('value', plan);
$('amount').setAttribute('value', amount);
});
});
&lt;/script&gt;

Thats it for now!

Parse for Links with Prototype JS

Parsing for links with the Prototype javascript library is easy. Here is the pattern for finding links

/(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^
=%&amp;amp;:/~\+#]*[\w\-\@?^=%&amp;amp;/~\+#])?/

And to implement it you can loop through your containers that might contain links

document.observe("dom:loaded", function(){
var posts = $$("div#posts");
for(var i = 0; i &lt; posts.length; i++){
var link_regex = /(http|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^
=%&amp;amp;:/~\+#]*[\w\-\@?^=%&amp;amp;/~\+#])?/;
var parsed_string = posts[i].innerHTML.gsub(link_regex, '&lt;a href="#{0}"
target="_blank"&gt;#{0}&lt;/a&gt;');
posts[i].innerHTML = parsed_string;
}
});