form_tag_helper.rb 8.6 KB
Newer Older
1
require 'cgi'
2
require 'action_view/helpers/tag_helper'
3 4 5 6 7

module ActionView
  module Helpers
    # Provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like
    # FormHelper does. With the FormTagHelper, you provide the names and values yourself.
8
    #
D
David Heinemeier Hansson 已提交
9
    # NOTE: The html options disabled, readonly, and multiple can all be treated as booleans. So specifying <tt>:disabled => true</tt>
10
    # will give <tt>disabled="disabled"</tt>.
11
    module FormTagHelper
12
      # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
13 14
      # ActionController::Base#url_for. The method for the form defaults to POST.
      #
15 16 17 18 19 20 21 22 23 24 25 26 27
      # Examples:
      # * <tt>form_tag('/posts') => <form action="/posts" method="post"></tt>
      # * <tt>form_tag('/posts/1', :method => :put) => <form action="/posts/1" method="put"></tt>
      # * <tt>form_tag('/upload', :multipart => true) => <form action="/upload" method="post" enctype="multipart/form-data"></tt>
      # 
      # ERb example:
      #   <% form_tag '/posts' do -%>
      #     <div><%= submit_tag 'Save' %></div>
      #   <% end -%>
      #
      # Will output:
      #   <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
      #
28 29
      # Options:
      # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
30 31 32
      # * <tt>:method</tt>    - The method to use when submitting the form, usually either "get" or "post".
      #                         If "put", "delete", or another verb is used, a hidden input with name _method 
      #                         is added to simulate the verb over post.
33
      def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
34
        html_options = html_options_for_form(url_for_options, options, *parameters_for_url)
35
        if block_given?
36
          form_tag_in_block(html_options, &block)
37
        else
38
          form_tag_html(html_options)
39
        end
40 41 42 43 44 45 46 47
      end

      alias_method :start_form_tag, :form_tag

      # Outputs "</form>"
      def end_form_tag
        "</form>"
      end
48
      
49
      deprecate :end_form_tag, :start_form_tag => :form_tag
50

51 52 53 54 55 56 57 58 59 60 61 62
      # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
      # choice selection box.
      #
      # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
      # associated records.
      #
      # <tt>option_tags</tt> is a string containing the option tags for the select box:
      #   # Outputs <select id="people" name="people"><option>David</option></select>
      #   select_tag "people", "<option>David</option>"
      #
      # Options:
      # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
63
      def select_tag(name, option_tags = nil, options = {})
64
        content_tag :select, option_tags, { "name" => name, "id" => name }.update(options.stringify_keys)
65 66
      end

67 68 69 70 71 72 73 74
      # Creates a standard text field.
      #
      # Options:
      # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
      # * <tt>:size</tt> - The number of visible characters that will fit in the input.
      # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
      # 
      # A hash of standard HTML options for the tag.
75
      def text_field_tag(name, value = nil, options = {})
76
        tag :input, { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
77 78
      end

79 80 81
      # Creates a hidden field.
      #
      # Takes the same options as text_field_tag
82
      def hidden_field_tag(name, value = nil, options = {})
83
        text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
84 85
      end

86 87 88 89 90 91 92 93 94 95
      # Creates a file upload field.
      #
      # If you are using file uploads then you will also need to set the multipart option for the form:
      #   <%= form_tag { :action => "post" }, { :multipart => true } %>
      #     <label for="file">File to Upload</label> <%= file_field_tag "file" %>
      #     <%= submit_tag %>
      #   <%= end_form_tag %>
      #
      # The specified URL will then be passed a File object containing the selected file, or if the field 
      # was left blank, a StringIO object.
96
      def file_field_tag(name, options = {})
97
        text_field_tag(name, nil, options.update("type" => "file"))
98 99
      end

100 101 102
      # Creates a password field.
      #
      # Takes the same options as text_field_tag
103
      def password_field_tag(name = "password", value = nil, options = {})
104
        text_field_tag(name, value, options.update("type" => "password"))
105 106
      end

107 108 109 110 111
      # Creates a text input area.
      #
      # Options:
      # * <tt>:size</tt> - A string specifying the dimensions of the textarea.
      #     # Outputs <textarea name="body" id="body" cols="25" rows="10"></textarea>
112
      #     <%= text_area_tag "body", nil, :size => "25x10" %>
113
      def text_area_tag(name, content = nil, options = {})
114 115 116 117
        options.stringify_keys!

        if size = options.delete("size")
          options["cols"], options["rows"] = size.split("x")
118
        end
119

120
        content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys)
121 122
      end

123
      # Creates a check box.
124
      def check_box_tag(name, value = "1", checked = false, options = {})
125
        html_options = { "type" => "checkbox", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
126
        html_options["checked"] = "checked" if checked
127
        tag :input, html_options
128 129
      end

130
      # Creates a radio button.
131
      def radio_button_tag(name, value, checked = false, options = {})
132 133
        pretty_tag_value = value.to_s.gsub(/\s/, "_").gsub(/(?!-)\W/, "").downcase
        html_options = { "type" => "radio", "name" => name, "id" => "#{name}_#{pretty_tag_value}", "value" => value }.update(options.stringify_keys)
134
        html_options["checked"] = "checked" if checked
135
        tag :input, html_options
136 137
      end

138 139
      # Creates a submit button with the text <tt>value</tt> as the caption. If options contains a pair with the key of "disable_with",
      # then the value will be used to rename a disabled version of the submit button.
140
      def submit_tag(value = "Save changes", options = {})
141 142 143
        options.stringify_keys!
        
        if disable_with = options.delete("disable_with")
144
          options["onclick"] = [
145
            "this.setAttribute('originalValue', this.value)",
146 147 148
            "this.disabled=true",
            "this.value='#{disable_with}'",
            "#{options["onclick"]}",
149
            "return (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
150
          ].join(";")
151 152 153
        end
          
        tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
154
      end
155
      
156 157 158
      # Displays an image which when clicked will submit the form.
      #
      # <tt>source</tt> is passed to AssetTagHelper#image_path
159
      def image_submit_tag(source, options = {})
160
        tag :input, { "type" => "image", "src" => image_path(source) }.update(options.stringify_keys)
161
      end
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
      
      private
        def html_options_for_form(url_for_options, options, *parameters_for_url)
          returning options.stringify_keys do |html_options|
            html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
            html_options["action"]  = url_for(url_for_options, *parameters_for_url)
          end
        end
        
        def extra_tags_for_form(html_options)
          case method = html_options.delete("method").to_s
            when /^get$/i # must be case-insentive, but can't use downcase as might be nil
              html_options["method"] = "get"
              ''
            when /^post$/i, "", nil
              html_options["method"] = "post"
              ''
            else
              html_options["method"] = "post"
              content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method), :style => 'margin:0;padding:0')
          end
        end
        
        def form_tag_html(html_options)
          extra_tags = extra_tags_for_form(html_options)
          tag(:form, html_options, true) + extra_tags
        end
        
        def form_tag_in_block(html_options, &block)
          content = capture(&block)
          concat(form_tag_html(html_options), block.binding)
          concat(content, block.binding)
          concat("</form>", block.binding)
        end
196 197 198
    end
  end
end