ActionView还带有许多helpers,让我们开发起来更快速更顺手,让我们来看看form中常用的一些helper方法
form_helper.rb:
[code]
module ActionView
module Helpers
module FormHelper
def form_for(object_name, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
options = args.last.is_a?(Hash) ? args.pop : {}
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
fields_for(object_name, *(args << options), &proc)
concat('</form>', proc.binding)
end
def fields_for(object_name, *args, &block)
raise ArgumentError, "Missing block" unless block_given?
options = args.last.is_a?(Hash) ? args.pop : {}
object = args.first
builder = options[:builder] || ActionView::Base.default_form_builder
yield builder.new(object_name, object, self, options, block)
end
def text_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options)
end
def password_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("password", options)
end
def hidden_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("hidden", options)
end
def file_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("file", options)
end
def text_area(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_text_area_tag(options)
end
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
end
def radio_button(object_name, method, tag_value, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
end
end
class InstanceTag
def to_input_field_tag(field_type, options = {})
options = options.stringify_keys
options["size"] ||= options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"]
options = DEFAULT_FIELD_OPTIONS.merge(options)
if field_type == "hidden"
options.delete("size")
end
options["type"] = field_type
options["value"] ||= value_before_type_cast(object) unless field_type == "file"
add_default_name_and_id(options)
tag("input", options)
end
def to_radio_button_tag(tag_value, options = {})
options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
options["type"] = "radio"
options["value"] = tag_value
if options.has_key?("checked")
cv = options.delete "checked"
checked = cv == true || cv == "checked"
else
checked = self.class.radio_button_checked?(value(object), tag_value)
end
options["checked"] = "checked" if checked
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
options["id"] ||= defined?(@auto_index) ?
"#{@object_name}_#{@auto_index}_#{@method_name}_#{pretty_tag_value}" :
"#{@object_name}_#{@method_name}_#{pretty_tag_value}"
add_default_name_and_id(options)
tag("input", options)
end
def to_text_area_tag(options = {})
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
add_default_name_and_id(options)
if size = options.delete("size")
options["cols"], options["rows"] = size.split("x")
end
content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options)
end
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
options["value"] = checked_value
if options.has_key?("checked")
cv = options.delete "checked"
checked = cv == true || cv == "checked"
else
checked = self.class.check_box_checked?(value(object), checked_value)
end
options["checked"] = "checked" if checked
add_default_name_and_id(options)
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value)
end
end
class FormBuilder
class_inheritable_accessor :field_helpers
self.field_helpers = (FormHelper.instance_methods - ['form_for'])
attr_accessor :object_name, :object, :options
def initialize(object_name, object, template, options, proc)
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
end
(field_helpers - %w(check_box radio_button)).each do |selector|
src = <<-end_src
def #{selector}(method, options = {})
@template.send(#{selector.inspect}, @object_name, method, options.merge(:object => @object))
end
end_src
class_eval src, __FILE__, __LINE__
end
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@template.check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
end
def radio_button(method, tag_value, options = {})
@template.radio_button(@object_name, method, tag_value, options.merge(:object => @object))
end
end
end
class Base
cattr_accessor :default_form_builder
self.default_form_builder = ::ActionView::Helpers::FormBuilder
end
end
[/code]
form_for方式引入,form_tag可以丢弃了,因为form_for是对form_tag更简洁更高级的封装
[code]
<% form_for :person, @person, :url => { :action => "update" } do |f| %>
First name: <%= f.text_field :first_name %>
Last name : <%= f.text_field :last_name %>
Biography : <%= f.text_area :biography %>
Admin? : <%= f.check_box :admin %>
<% end %>
[/code]
而text_field/password_field/hidden_field/file_field/text_area/check_box/radio_button以及form_options_helpers里的select这八项
form里的基本元素都有方便的helper方法定义,实际上是通过InstanceTag的to_input_field_tag/to_text_area_tag等方法来生成html标签
而InstanceTag的to_input_field_tag/to_text_area_tag等方法又是通过tag_helper.rb里定义的content_tag方法来生成html标签,都很简单
我们可以定制自己的FormBuilder来满足form样式一样等要求,详见[url=http://hideto.iteye.com/blog/74153]每天一剂Rails良药之Creating a Custom Form Builder[/url]
form_helper.rb:
[code]
module ActionView
module Helpers
module FormHelper
def form_for(object_name, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
options = args.last.is_a?(Hash) ? args.pop : {}
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
fields_for(object_name, *(args << options), &proc)
concat('</form>', proc.binding)
end
def fields_for(object_name, *args, &block)
raise ArgumentError, "Missing block" unless block_given?
options = args.last.is_a?(Hash) ? args.pop : {}
object = args.first
builder = options[:builder] || ActionView::Base.default_form_builder
yield builder.new(object_name, object, self, options, block)
end
def text_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options)
end
def password_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("password", options)
end
def hidden_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("hidden", options)
end
def file_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("file", options)
end
def text_area(object_name, method, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_text_area_tag(options)
end
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
end
def radio_button(object_name, method, tag_value, options = {})
InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
end
end
class InstanceTag
def to_input_field_tag(field_type, options = {})
options = options.stringify_keys
options["size"] ||= options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"]
options = DEFAULT_FIELD_OPTIONS.merge(options)
if field_type == "hidden"
options.delete("size")
end
options["type"] = field_type
options["value"] ||= value_before_type_cast(object) unless field_type == "file"
add_default_name_and_id(options)
tag("input", options)
end
def to_radio_button_tag(tag_value, options = {})
options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
options["type"] = "radio"
options["value"] = tag_value
if options.has_key?("checked")
cv = options.delete "checked"
checked = cv == true || cv == "checked"
else
checked = self.class.radio_button_checked?(value(object), tag_value)
end
options["checked"] = "checked" if checked
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
options["id"] ||= defined?(@auto_index) ?
"#{@object_name}_#{@auto_index}_#{@method_name}_#{pretty_tag_value}" :
"#{@object_name}_#{@method_name}_#{pretty_tag_value}"
add_default_name_and_id(options)
tag("input", options)
end
def to_text_area_tag(options = {})
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
add_default_name_and_id(options)
if size = options.delete("size")
options["cols"], options["rows"] = size.split("x")
end
content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options)
end
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
options["value"] = checked_value
if options.has_key?("checked")
cv = options.delete "checked"
checked = cv == true || cv == "checked"
else
checked = self.class.check_box_checked?(value(object), checked_value)
end
options["checked"] = "checked" if checked
add_default_name_and_id(options)
tag("input", options) << tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value)
end
end
class FormBuilder
class_inheritable_accessor :field_helpers
self.field_helpers = (FormHelper.instance_methods - ['form_for'])
attr_accessor :object_name, :object, :options
def initialize(object_name, object, template, options, proc)
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
end
(field_helpers - %w(check_box radio_button)).each do |selector|
src = <<-end_src
def #{selector}(method, options = {})
@template.send(#{selector.inspect}, @object_name, method, options.merge(:object => @object))
end
end_src
class_eval src, __FILE__, __LINE__
end
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
@template.check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
end
def radio_button(method, tag_value, options = {})
@template.radio_button(@object_name, method, tag_value, options.merge(:object => @object))
end
end
end
class Base
cattr_accessor :default_form_builder
self.default_form_builder = ::ActionView::Helpers::FormBuilder
end
end
[/code]
form_for方式引入,form_tag可以丢弃了,因为form_for是对form_tag更简洁更高级的封装
[code]
<% form_for :person, @person, :url => { :action => "update" } do |f| %>
First name: <%= f.text_field :first_name %>
Last name : <%= f.text_field :last_name %>
Biography : <%= f.text_area :biography %>
Admin? : <%= f.check_box :admin %>
<% end %>
[/code]
而text_field/password_field/hidden_field/file_field/text_area/check_box/radio_button以及form_options_helpers里的select这八项
form里的基本元素都有方便的helper方法定义,实际上是通过InstanceTag的to_input_field_tag/to_text_area_tag等方法来生成html标签
而InstanceTag的to_input_field_tag/to_text_area_tag等方法又是通过tag_helper.rb里定义的content_tag方法来生成html标签,都很简单
我们可以定制自己的FormBuilder来满足form样式一样等要求,详见[url=http://hideto.iteye.com/blog/74153]每天一剂Rails良药之Creating a Custom Form Builder[/url]