Rails Select Tag and Onchange Event Calling a Remote Function with Default Option Selected

Here is a little code snippet that will fire off a request to update_client_path when you change the select field. This stands alone, rather than being apart of a larger form. The Client::CATEGORY argument is a hash, which I populated manually in my Client.rb model. It is something like this

CATEGORY = {:lead => "Prospective clients", :past => "Previous clients..."}

In my view I have

<%= select_tag "client_category",
 options_for_select(Client::CATEGORY.keys, @client.category.to_sym),
 { :onchange => remote_function( :url => update_client_path(@client), :with=>"'change_to='+this.value+''" ) } %>

One gotcha is that the select_tag, options_for_select argument expects a symbol if you’re passing in a hash as the type of the collection! It may not set the default if you provide it with a string for the second arg (which specifies which option should be set as selected).

You also need to make sure that the url update_client_path is set in your routes file and that your controller (clients_controller in this case) has the appropriate method to handle the request.

Collection Select Helper and OnChange Event in Rails

Given a collection of Active Record objects, you may use the collection_select helper method to produce a select form field. You need to pass in a number of arguments to the helper function.

1) object – your model object used in the collection
2) method – a valid model attribute or method
3) collection – a collection of active record model objects
4) option_value – value being set from the model for the <option value=”option_value”> html element
5) option_name – what is displayed for the user e.g., <option> option_name <
6) option – general options
7) options for the select html element

# helper and arguments...
# collection_select( model, id, collection, option_value, option_name, options, html_options)

&lt;%= collection_select("states", "state_id",
  State.participating,
  "abbreviation", "name",
   {:selected=&gt; get_current_state_or_nil },
   {:onchange=&gt;"document.location='/states/'+this.value"}
) %&gt;

#which will produce something like...

<pre id="line27">&lt;select id="state_id" name="state[id]" onchange="document.location='/states/'+this.value"&gt;
&lt;option value="AL"&gt;Alabama&lt;/option&gt;
&lt;option value="AK"&gt;Alaska&lt;/option&gt;
&lt;option value="AZ"&gt;Arizona&lt;/option&gt;
&lt;option value="AR"&gt;Arkansas&lt;/option&gt;
&lt;/select&gt;

Yield a Block within Rails Helper Method with Multiple Content_tags Using Concat

To clean up some repetitive html coding in views, pass a block of text to a helper function which will wrap it for you the same way, each and every time. For example, I have a ‘help’ link which will toggle the display of a block of text if a link is clicked. I use this “+/- help” link throughout my application in various views. I could create a partial but this gets messy. Instead this is what I want to write in my views…

&lt;% help do %&gt;
  here is my help text...
&lt;% end %&gt;

which will render HTML similar to this…

&lt;a href='#' 'class='help' id='help_link_123456' onclick='some_func_to_toggle_state'&gt;+/- help&lt;/a&gt;
&lt;div id="help_123456"&gt;
  here is my help text...
&lt;/div&gt;

In order to accomplish this you need to create a helper method. Assigning a unique id, just a random number, will help avoid collisions with the state of the toggled div if you use this more than once per page. Placing other HTML helper methods inside the concat() method allows multiple tags as well as rendering the block passed to the function in the appropriate place. It looks a little unwieldy, but works nicely.

# app/helpers/application_helper.rb
module ApplicationHelper
  def help(&block)
    uniqid = rand; concat( link_to_function("+/- help") do |page|
      page["help_#{uniqid}"].toggle
      page.visual_effect :highlight, "help_#{uniqid}"
    end + content_tag(:div,:class=>"help",:id=>"help_#{uniqid}", :style=>"display:none") do
      yield
    end )
  end
end

Rails Helper to Remove Leading Zero in 12 Hour Time Format

I can’t find a strftime() format that will output the hour without the leading zero. For instance 6:20 will be instead 06:20. This just looks a little sloppy. I created a datetime.rb intializer which will contain custom datetime formats for my application.

# RAILS_ROOT/config/initializers/datetime.rb
Time::DATE_FORMATS[:date] = "%a %b %d, %Y"
Time::DATE_FORMATS[:time] = "%I:%M%p"
# ...

Next I set up a helper function which will string together the :date and :time formats and remove the leading zero in the hour with the .gsub method on the string.

  # RAILS_ROOT/app/helpers/application_helper.rb
  def fulltime(created_at)
    created_at.to_s(:date)+" "+created_at.to_s(:time).gsub(/^0/,'').downcase
  end

In a view I’ll just pass the timestamp to the fulltime function

fulltime(@post.created_at)

Date and Time Helpers in Rails

Just for reference http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html#M001695

This post was created about <%= time_ago_in_words @post.created_at %> ago

Make Rails Lib Module Methods Available to Views

If you create a module in the lib/ directory of your Rails application you won’t have access to those methods in your views. If you don’t want to put those methods in a helper file, you need to add a method to your module that makes them available for you views like so…

#in lib/my_module_name.rb
module MyModuleName
  def my_method_for_views
     #logic...
  end
  def self.included(base)
    base.send :helper_method, :my_method_for_views if base.respond_to? :helper_method
  end
end

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