Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix radio button label #63

Closed
wants to merge 8 commits into from

3 participants

Dan Kubb Stephen Touset Troex Nevelin
Dan Kubb

The radio button label isn't being associated with the correct radio button because it needs to be passed the value too. This fix makes sure the value is provided to the label.

Dan Kubb

Sorry, I meant to submit this to the bootstrap-2.0 branch. Closing and will reopen with correct PR.

Dan Kubb dkubb closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 24, 2012
  1. Update for Bootstrap 2.0

    authored
  2. Update examples

    authored
  3. Update version to 2.0.1

    authored
  4. Remove known bugs section

    authored
Commits on Jul 29, 2012
  1. Troex Nevelin
Commits on Jul 31, 2012
  1. Merge pull request #62 from troex/fix-60

    authored
    a better fix for #60, honors :append as well
Commits on Aug 4, 2012
  1. Dan Kubb
This page is out of date. Refresh to see the latest.
46 README.markdown
View
@@ -18,10 +18,11 @@ Just Rails. But you were going to use that anyway, weren't you?
## Syntax ##
```haml
-= twitter_bootstrap_form_for @user do |user|
+/ supports both vertical and horizontal forms
+= twitter_bootstrap_form_for @user, :html => { :class => 'form-horizontal'} do |user|
/ wraps a section in a fieldset with the provided legend text
- = user.inputs 'Sign up', :class => 'sign_up' do
+ = user.fieldset 'Sign up', :class => 'sign_up' do
/ generates a standard email field
= user.email_field :email, :placeholder => 'me@example.com'
@@ -40,29 +41,29 @@ Just Rails. But you were going to use that anyway, weren't you?
%span.add-on @
/ select fields now have the second parameter as a label
- = user.date_select :born_on, 'Born on', {}, :class => 'small'
+ = user.date_select :born_on, 'Born on', {}, :class => 'span2'
- / inline inputs are not automatically labeled
- = user.inline 'Interests' do |inline|
- #{inline.text_field :interest_1, :class => 'small'},
- #{inline.text_field :interest_2, :class => 'small'}, and
- #{inline.text_field :interest_3, :class => 'small'}
+ / inline inputs
+ = user.label 'Interests' do |controls|
+ #{controls.text_field :interest_1, :class => 'span2 inline'},
+ #{controls.text_field :interest_2, :class => 'span2 inline'}, and
+ #{controls.text_field :interest_3, :class => 'span2 inline'}
/ group of radio buttons
- = user.toggles 'Email Preferences' do
- = user.radio_button :email, 'HTML Email', :html, :checked => true
- = user.radio_button :email, 'Plain Text', :plain
+ = user.label 'Email Preferences' do |controls|
+ = controls.radio_button :email, :html, 'HTML Email', :checked => true
+ = controls.radio_button :email, :plain, 'Plain Text'
/ group of checkboxes
- = user.toggles 'Agreements' do
- = user.check_box :agree, 'I agree to the abusive Terms and Conditions'
- = user.check_box :spam, 'I agree to receive all sorts of spam'
- = user.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
-
- / wraps buttons in a distinctive style
- = user.actions do
- = user.submit 'Sign up'
- = button_tag 'Cancel', :type => 'reset', :class => 'btn'
+ = user.label 'Agreements' do |controls|
+ = controls.check_box :agree, 'I agree to the abusive Terms and Conditions'
+ = controls.check_box :spam, 'I agree to receive all sorts of spam'
+ = controls.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
+
+ / wraps buttons in a distinctive style
+ = user.actions do
+ = user.submit 'Sign up'
+ = user.button 'Cancel'
```
That code produces the following output, with no custom stylesheets.
@@ -82,9 +83,4 @@ simple:
* the last options hash accepts an `:add_on` key
* if a block is passed, the HTML it outputs is placed immediately after the input
-## Known Bugs ##
-
- - inline fields don't receive error markup ([issue #28])
-
[Twitter Bootstrap]: http://twitter.github.com/bootstrap/
-[issue #28]: https://github.com/stouset/twitter_bootstrap_form_for/issues/28
BIN  examples/screenshot.png
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
218 lib/twitter_bootstrap_form_for/form_builder.rb
View
@@ -26,32 +26,59 @@ class TwitterBootstrapFormFor::FormBuilder < ActionView::Helpers::FormBuilder
# Wraps the contents of the block passed in a fieldset with optional
# +legend+ text.
#
- def inputs(legend = nil, options = {}, &block)
+ def fieldset(legend = nil, options = {})
template.content_tag(:fieldset, options) do
template.concat template.content_tag(:legend, legend) unless legend.nil?
- block.call
+ yield
end
end
#
- # Wraps groups of toggles (radio buttons, checkboxes) with a single label
- # and the appropriate markup. All toggle buttons should be rendered
- # inside of here, and will not look correct unless they are.
+ # Wraps action buttons into their own styled container.
+ #
+ def actions(&block)
+ template.content_tag(:div, :class => 'form-actions', &block)
+ end
+
#
- def toggles(label = nil, &block)
- template.content_tag(:div, :class => 'clearfix') do
- template.concat template.content_tag(:label, label)
- template.concat template.content_tag(:div, :class => "input") {
- template.content_tag(:ul, :class => "inputs-list") { block.call }
+ # Attaches a label to the inputs rendered inside of the block passed to it.
+ # Associates the label with the input for the +attribute+ given. If +text+
+ #is passed, uses that as the text for the label; otherwise humanizes the
+ # +attribute+ name.
+ #
+ def label(attribute, text = '', options = {}, &block)
+ text, attribute = attribute, nil if attribute.kind_of? String
+
+ options = { :class => 'control-label' }.merge(options)
+ id = _wrapper_id attribute, 'control_group'
+ classes = _wrapper_classes attribute, 'control-group'
+
+ template.content_tag(:div, :id => id, :class => classes) do
+ template.concat case
+ when attribute && text then super(attribute, text, options, &nil)
+ when attribute then super(attribute, nil, options, &nil)
+ when text then template.label_tag(nil, text, options, &nil)
+ end
+
+ template.concat template.content_tag(:div, :class => 'controls') {
+ template.fields_for(
+ self.object_name,
+ self.object,
+ self.options.merge(:builder => TwitterBootstrapFormFor::FormControls),
+ &block
+ )
}
end
end
#
- # Wraps action buttons into their own styled container.
+ # Renders a button with default classes to style it as a form button.
#
- def actions(&block)
- template.content_tag(:div, :class => 'actions', &block)
+ def button(value = nil, options = {})
+ super value, {
+ :type => 'button',
+ :class => 'btn',
+ }.merge(options)
end
#
@@ -59,119 +86,50 @@ def actions(&block)
# button.
#
def submit(value = nil, options = {})
- options[:class] ||= 'btn primary'
-
- super value, options
- end
-
- #
- # Creates bootstrap wrapping before yielding a plain old rails builder
- # to the supplied block.
- #
- def inline(label = nil, &block)
- template.content_tag(:div, :class => 'clearfix') do
- template.concat template.content_tag(:label, label) if label.present?
- template.concat template.content_tag(:div, :class => 'input') {
- template.content_tag(:div, :class => 'inline-inputs') do
- template.fields_for(
- self.object_name,
- self.object,
- self.options.merge(:builder => ActionView::Helpers::FormBuilder),
- &block
- )
- end
- }
- end
+ self.button value, {
+ :type => 'submit',
+ :class => 'btn btn-primary',
+ }.merge(options)
end
INPUTS.each do |input|
define_method input do |attribute, *args, &block|
- options = args.extract_options!
- label = args.first.nil? ? '' : args.shift
- classes = [ 'input' ]
- classes << ('input-' + options.delete(:add_on).to_s) if options[:add_on]
-
- self.div_wrapper(attribute) do
- template.concat self.label(attribute, label) if label
- template.concat template.content_tag(:div, :class => classes.join(' ')) {
- template.concat super(attribute, *(args << options))
- template.concat error_span(attribute)
- block.call if block.present?
- }
- end
- end
- end
+ options = args.extract_options!
+ text = args.any? ? args.shift : ''
- TOGGLES.each do |toggle|
- define_method toggle do |attribute, *args, &block|
- label = args.first.nil? ? '' : args.shift
- target = self.object_name.to_s + '_' + attribute.to_s
- label_attrs = toggle == :check_box ? { :for => target } : {}
-
- template.content_tag(:li) do
- template.concat template.content_tag(:label, label_attrs) {
- template.concat super(attribute, *args)
- template.concat ' ' # give the input and span some room
- template.concat template.content_tag(:span, label)
- }
+ self.label(attribute, text) do |builder|
+ builder.send(input, attribute, *(args << options), &block)
end
end
end
protected
- #
- # Wraps the contents of +block+ inside a +tag+ with an appropriate class and
- # id for the object's +attribute+. HTML options can be overridden by passing
- # an +options+ hash.
- #
- def div_wrapper(attribute, options = {}, &block)
- options[:id] = _wrapper_id attribute, options[:id]
- options[:class] = _wrapper_classes attribute, options[:class], 'clearfix'
-
- template.content_tag :div, options, &block
- end
-
- def error_span(attribute, options = {})
- options[:class] ||= 'help-inline'
-
- template.content_tag(
- :span, self.errors_for(attribute),
- :class => options[:class]
- ) if self.errors_on?(attribute)
- end
-
def errors_on?(attribute)
self.object.errors[attribute].present? if self.object.respond_to?(:errors)
end
- def errors_for(attribute)
- self.object.errors[attribute].try(:join, ', ')
- end
-
private
#
# Returns an HTML id to uniquely identify the markup around an input field.
- # If a +default+ is provided, it uses that one instead.
#
- def _wrapper_id(attribute, default = nil)
- default || [
+ def _wrapper_id(attribute, suffix = nil)
+ [
_object_name + _object_index,
_attribute_name(attribute),
- 'input'
- ].join('_')
+ suffix,
+ ].compact.join('_') if attribute
end
#
# Returns any classes necessary for the wrapper div around fields for
# +attribute+, such as 'errors' if any errors are present on the attribute.
- # This merges any +classes+ passed in.
+ # Merges any +classes+ passed in.
#
def _wrapper_classes(attribute, *classes)
- classes.compact.tap do |klasses|
- klasses.push 'error' if self.errors_on?(attribute)
- end.join(' ')
+ classes.push 'error' if attribute and self.errors_on?(attribute)
+ classes.compact.join(' ')
end
def _attribute_name(attribute)
@@ -190,3 +148,69 @@ def _object_index
end.to_s
end
end
+
+class TwitterBootstrapFormFor::FormControls < ActionView::Helpers::FormBuilder
+ attr_reader :template
+ attr_reader :object
+ attr_reader :object_name
+
+ TwitterBootstrapFormFor::FormBuilder::INPUTS.each do |input|
+ define_method input do |attribute, *args, &block|
+ options = args.extract_options!
+ add_on = options.delete(:add_on)
+ tag = add_on.present? ? :div : :span
+ classes = [ "input", add_on ].compact.join('-')
+
+ template.content_tag(tag, :class => classes) do
+ block.call if block.present? && add_on == :prepend
+ template.concat super attribute, *(args << options)
+ block.call if block.present? && add_on != :prepend
+ template.concat self.error_span(attribute) if self.errors_on?(attribute)
+ end
+ end
+ end
+
+ def check_box(attribute, text, options = {}, checked_value = 1, unchecked_value = 0)
+ klasses = _merge_classes 'checkbox', options.delete(:inline) && 'inline'
+
+ self.label(attribute, :class => klasses) do
+ template.concat super(attribute, options, checked_value, unchecked_value)
+ template.concat text
+ yield if block_given?
+ end
+ end
+
+ def radio_button(attribute, value, text = nil, options = {})
+ klasses = _merge_classes 'radio', options.delete(:inline) && 'inline'
+
+ self.label(attribute, :value => value, :class => klasses) do
+ template.concat super(attribute, value, options)
+ template.concat text || value.to_s.humanize.titleize
+ yield if block_given?
+ end
+ end
+
+ protected
+
+ def error_span(attribute, options = {})
+ options[:class] = _merge_classes options[:class], 'help-inline'
+
+ template.content_tag :span,
+ self.errors_for(attribute),
+ :class => options[:class]
+ end
+
+ def errors_for(attribute)
+ self.object.errors[attribute].try(:join, ', ')
+ end
+
+ def errors_on?(attribute)
+ self.object.errors[attribute].present? if self.object.respond_to?(:errors)
+ end
+
+ private
+
+ def _merge_classes(string, *classes)
+ string.to_s.split(' ').push(*classes.compact).join(' ')
+ end
+end
2  lib/twitter_bootstrap_form_for/form_helpers.rb
View
@@ -18,7 +18,7 @@ module TwitterBootstrapFormFor::FormHelpers
private
- BLANK_FIELD_ERROR_PROC = lambda {|input, _| input }
+ BLANK_FIELD_ERROR_PROC = lambda {|input, *_| input }
def _override_field_error_proc
original_field_error_proc = ::ActionView::Base.field_error_proc
2  lib/twitter_bootstrap_form_for/version.rb
View
@@ -1,3 +1,3 @@
module TwitterBootstrapFormFor
- VERSION = '1.0.5'
+ VERSION = '2.0.1.0.rc1'
end
Something went wrong with that request. Please try again.