Add file-wide comments for rdoc's Files section, and add a comment to the FormHelper module explaining the form user experience
- ~
- rails_plugins
- templated_attribute
- lib
- templated_attribute_helper.rb
| templated_attribute_helper.rb |
|---|
# This file contains the extensions to ActionView::Helpers::FormHelper:
# +text_area_with_templating+[link:classes/ActionView/Helpers/FormHelper.html]
# and +text_field_with_templating+[link:classes/ActionView/Helpers/FormHelper.html].
module ActionView #:nodoc:
module Helpers #:nodoc:
# Forms which have fields for templated attributes have the following behavior:
# * When the user visits the form, the field is pre-filled with a
# template value (like <tt>http://</tt>) if the real value is empty or +nil+.
# * If the template is left unchanged, it's convert it to +nil+ before validation.
# * To imply that the initial value is a suggestion, the field's text color is
# gray until the user makes a change. If the user's only change is to empty
# the field, we should reset it to the template value and gray again on blur.
# * In addition to this, <tt>label</tt>-style templates blank their fields
# (removing the template value) when they receive focus.
module FormHelper
# When this plugin is installed, calls to
# <tt>ActionView::Helpers::FormHelper#text_area</tt> in your forms will
# transparently call this method instead.
#
# When you call <tt>#text_area</tt> for an attribute which is declared as
# templated using
# +templated_attribute+[link:classes/TemplatedAttribute/ActiveRecordExtensions/ClassMethods.html]
# in the model, we enhance the normal <tt>#text_area</tt>
# by inserting templated values insetad of empty field values, and
# appending unobtrusive Javascript to enable smart templating behavior
# and styling on <tt><textarea></tt> tags.
#
# When you call <tt>#text_area</tt> for attributes which are not
# templated, we defer to Rails's original <tt>#text_area</tt> without
# enhhancement.
#
# Javascript support will be disabled if <tt>:templated_javascript =>
# false</tt> is passed in the options hash of +text_area+.
#
# <b>Note:</b> The Javascript uses Prototype to add event handlers to
# your form. Make sure <tt>prototype.js</tt> is included in your layout.
#
# Usage:
# <% form_for @user do |form| %>
# <label for="user_website">About you:</label>
# <%= form.text_area :bio %>
# <% end %>
def text_area_with_templating(class_name, method, options={})
args = [class_name, method, options] # use params above for docs' sake
if templated?(*args)
field_with_templating :text_area, *args
else
text_area_without_templating *args
end
end
alias_method_chain :text_area, :templating
# Same as <tt>#text_area_with_templating</tt>, but overrides
# <tt>ActionView::Helpers::FormHelper#text_field</tt> so you can get
# templated attribute behavior with <tt><input type="text"></tt> tags.
# See notes for <tt>#text_area_with_templating</tt> above.
#
# Usage:
# <% form_for @user do |form| %>
# <label for="user_website">Website:</label>
# <%= form.text_field :website %>
#
# <label for="user_phone">Phone:</label>
# <%= form.text_field :phone, :templated_javascript => false %>
# <% end %>
def text_field_with_templating(class_name, method, options={})
args = [class_name, method, options] # use params above for docs' sake
if templated?(*args)
field_with_templating :text_field, *args
else
text_field_without_templating *args
end
end
alias_method_chain :text_field, :templating
private
# Helper method to determine whether or not +templated_attribute+ has
# been called for a given class and method.
def templated?(class_name, method, options={}) # :nodoc:
klass = class_name.classify.constantize
klass.respond_to?(:templated_attributes_options) && klass.templated_attributes_options[method.to_sym]
end
# Behind the scenes, figure out the starting value for the field, then
# render the field normally with that value (if applicable) and append a
# touch of Javascript to add behavior.
def field_with_templating(field_helper, class_name, method, options = {}) # :nodoc:
options = { :templated_javascript => true }.merge(options)
template_options = class_name.classify.constantize.templated_attributes_options[method.to_sym]
# If calling method on object returns nil, use template value instead
field_value = options[:object].__send__(method.to_sym)
if field_value.nil? || field_value.strip.empty?
field_value = template_options[:value]
end
# Only use javascript if not explicitly disabled in options
if options.delete(:templated_javascript)
javascript_string = javascript_tag \
"Event.observe(window, 'load', function() { new TemplatedAttribute($('#{class_name}_#{method}'),
'#{template_options[:type]}', '#{template_options[:value]}'); });"
else
javascript_string = ''
end
# output using the original method with our modified form field value,
# plus the javascript
send("#{field_helper}_without_templating".to_sym, class_name, method, options.merge({:value => field_value})) + javascript_string
end
end
end
end
