avatar

17

* finish implementation (switching to an `alias_method_chain`-based extension for #text_area and #text_field)
* add lots of rdocs
* change design so templated value is inserted by form helper, not javascript.
* add :javascript_templating => false option to disable javascript

by chrisk, 28 Jul, 2007 03:34 AM
6 17  
2828     this.template_type = template_type;
2929     this.template_value = template_value;
3030     
31     // load template value into possibly-empty field initially
32     this.blur();
31     // load template styling into possibly-templated fields initially
32     this.initStyles();
3333     
3434     // register listeners for keyUp and blur to monitor changes to the field;
3535     // also register focus for 'label' template types
------
3939       Event.observe(element, 'focus', this.focus.bindAsEventListener(this));
4040     }
4141   },
42   
43   // initStyles: check for existing template values and style them correctly
44   initStyles: function() {
45     this.keyUp();  // happens to be the same code
46   },
4247 
4348   // keyup: if the field's value is the template value, style it correctly
4449   keyUp: function() {
14 17  
1 module ActionView
2   module Helpers
1 module ActionView         #:nodoc:
2   module Helpers          #:nodoc:
33     module FormHelper
44       
55       
6       # Overrides <tt>ActionView::Helpers::FormHelper#text_field</tt> to append
7       # templating behavior using unobtrusive javascript. Note, the javascript
8       # used requires Prototype to add <tt>onLoad</tt> event listeners.
9       def text_field_with_templating(*args)
10         field_with_templating :text_field, *args
6       # When this plugin is installed, calls to
7       # <tt>ActionView::Helpers::FormHelper#text_area</tt> in your forms will
8       # transparently call this method instead.
9       #
10       # When you call <tt>#text_area</tt> for an attribute which is declared as
11       # templated using 
12       # +templated_attribute+[link:classes/TemplatedAttribute/ActiveRecordExtensions/ClassMethods.html]
13       # in the model, we enhance the normal <tt>#text_area</tt>
14       # by inserting templated values insetad of empty field values, and
15       # appending unobtrusive Javascript to enable smart templating behavior
16       # and styling on <tt><textarea></tt> tags.
17       #
18       # When you call <tt>#text_area</tt> for attributes which are not
19       # templated, we defer to Rails's original <tt>#text_area</tt> without
20       # changes.
21       #
22       # Javascript support will be disabled if <tt>:templated_javascript =>
23       # false</tt> is passed in the options hash of +text_area+.
24       #
25       # <b>Note:</b> The Javascript uses Prototype to add event handlers to
26       # your form. Make sure <tt>prototype.js</tt> is included in your layout.
27       def text_area_with_templating(class_name, method, options={})
28         args = [class_name, method, options]  # use params above for docs' sake
29         if templated?(*args)
30           field_with_templating :text_area, *args
31         else
32           text_area_without_templating *args
33         end
1134       end
35       alias_method_chain :text_area, :templating
36       
37       
38       # Same as <tt>#text_area_with_templating</tt>, but overrides
39       # <tt>ActionView::Helpers::FormHelper#text_field</tt> so you can get
40       # templated attribute behavior with <tt><input type="text"></tt> tags.
41       # See notes for <tt>#text_area_with_templating</tt> above.
42       def text_field_with_templating(class_name, method, options={})
43         args = [class_name, method, options]  # use params above for docs' sake
44         if templated?(*args)
45           field_with_templating :text_field, *args
46         else
47           text_field_without_templating *args
48         end
49       end
1250       alias_method_chain :text_field, :templating
51       
1352 
1453 
1554 
16       # Overrides <tt>ActionView::Helpers::FormHelper#text_area</tt> to append
17       # templating behavior using unobtrusive javascript. Note, the javascript
18       # used requires Prototype to add <tt>onLoad</tt> event listeners.
19       def text_area_with_templating(*args)
20         field_with_templating :text_area, *args
55 
56       private
57       
58       
59       # Helper method to determine whether or not +templated_attribute+ has
60       # been called for a given class and method.
61       def templated?(class_name, method, options={})  # :nodoc:
62         klass = class_name.classify.constantize
63         klass.respond_to?(:templated_attributes_options) && klass.templated_attributes_options[method.to_sym]
2164       end
22       alias_method_chain :text_area, :templating
2365 
2466 
67       # Behind the scenes, figure out the starting value for the field, then
68       # render the field normally with that value (if applicable) and append a
69       # touch of Javascript to add behavior.
70       def field_with_templating(field_helper, class_name, method, options = {})   # :nodoc:
71         options = { :templated_javascript => true }.merge(options)
72         template_options = class_name.classify.constantize.templated_attributes_options[method.to_sym]
2573 
26 
27       private
28       
29       def field_with_templating(field_helper, object_name, method, options = {})
30         object = object_name.classify.constantize
31         template_options = object.templated_attributes_options[method.to_sym]
74         # If calling method on object returns nil, use template value instead
75         field_value = options[:object].__send__(method.to_sym)
76         if field_value.nil? || field_value.strip.empty?
77           field_value = template_options[:value]
78         end
3279         
33         send("#{field_helper}_without_templating".to_sym, object_name, method, options) +
34         javascript_tag("Event.observe(window, 'load', function() {
35                           new TemplatedAttribute($('#{object_name}_#{method}'),
36                                                  '#{template_options[:type]}',
37                                                  '#{template_options[:value]}');
38                         });")
80         # Only use javascript if not explicitly disabled in options
81         if options.delete(:templated_javascript)
82           javascript_string = javascript_tag \
83             "Event.observe(window, 'load', function() { new TemplatedAttribute($('#{class_name}_#{method}'),
84              '#{template_options[:type]}', '#{template_options[:value]}'); });"
85         else
86           javascript_string = ''
87         end
88         
89         # output using the original method with our modified form field value,
90         # plus the javascript
91         send("#{field_helper}_without_templating".to_sym, class_name, method, options.merge({:value => field_value})) + javascript_string
3992       end
4093 
4194