Luiz Daluz
Luiz Daluz

Reputation: 99

Rails 3 + simple form + bootstrap Radio and checkbox as button w/ toggle function

I'm trying to get my radios and checkboxes to work as buttons w/ the toggle function as seen on twitters bootstrap page. link!

I've somehow managed to get those buttons to appear and function w/ the database, but when If the user returns to the page, they are not toggled. I was wondering if that's is possible or not. If yes, how can I implement that on my code? I'm using simple_form + twitter bootstrap.

Here is the code I'm using to display the radio button:

<div class="btn-group" data-toggle="buttons-radio">
<%= f.input :child_gender, :label => "Child gender?", :collection => [['Boy', 'Boy'], ['Girl', 'Girl'], :as => :radio_buttons, :input_html => { :data => {:toggle => 'buttons-radio'} }, :item_wrapper_class => 'btn btn-toggle btn-small inline' %>
</div>

And here is the code for checkbox button:

<div class="btn-group">
<%= f.input :child_size, :label => "What size?", :collection => child_size, :as => :check_boxes, :input_html => { :data => {:toggle => 'buttons-checkbox'} }, :item_wrapper_class => 'btn btn-toggle btn-small' %>
</div>

Here is the custom css I have for btn-toggle:

.btn-toggle input {
    display: none;
}

Any help is appreciated.

Upvotes: 4

Views: 3344

Answers (3)

Darren Hicks
Darren Hicks

Reputation: 5076

Building from Nickl's answer and tweaking things to require no additional Javascript and to render the same semantic HTML that Bootstrap currently expects, here's my solution:

class ButtonRadioInput < SimpleForm::Inputs::CollectionRadioButtonsInput
  def input
    out = '<div class="btn-group" data-toggle="buttons">'
    label_method, value_method = detect_collection_methods
    collection.each do |item|
      value = item.send(value_method)
      label = item.send(label_method)
      active = ''
      active = ' active' unless
          out =~ / active/ ||
          input_html_options[:value] != value &&
          item != collection.last
      input_html_options[:value] = value unless active.empty?
      btn = 'btn'
      btn = "btn btn-#{item.third}" unless item.third.nil?
      out << <<-HTML
        <label class="#{btn} #{active}">    
          <input type="radio" value="#{value}" name="#{attribute_name}">#{label}</input>
        </label>
HTML
    end
    out << "</div>"
    out.html_safe
  end
end

And then to use this you would use SimpleForm as such:

=f.input :role, label: false, as: :button_radio, 
                    collection: [["Warm Up", :warm_up, :primary],
                                ["Small Sided", :small_sided, :primary],
                                ["Expanded", :expanded, :primary],
                                ["Game", :game, :primary]]

This will render code just like the radio-button examples from Bootstrap's own components page.

Upvotes: 4

nickl-
nickl-

Reputation: 8731

Here is the missing simple_form input we need for the bootstrap radio buttons.

# lib/inputs/button_radio_input.rb
class ButtonRadioInput < SimpleForm::Inputs::CollectionRadioButtonsInput
  def input
    out = <<-HTML

<div class="btn-group" data-toggle="buttons-radio">
HTML
    input_field = @builder.hidden_field(attribute_name, input_html_options)
    input_id = input_field[/ id="(\w*)/, 1]
    label_method, value_method = detect_collection_methods
    collection.each {|item|
      value = item.send(value_method)
      label = item.send(label_method)
      on_click = "document.getElementById('#{input_id}').value='#{value}';return false;"
      active = ''
      active = ' active' unless
          out =~ / active/ ||
          input_html_options[:value] != value &&
          item != collection.last
      input_html_options[:value] = value unless active.empty?
      btn = 'btn'
      btn = "btn btn-#{item.third}" unless item.third.nil?
      out << <<-HTML
  <button onclick="javascript:#{on_click}" type="button" class="#{btn}#{active}">#{label}</button>
HTML
    }
    value = <<-VAL
value="#{input_html_options[:value]}"
VAL
    input_field[/value="[^"]*"/] = value.chomp if input_field =~ /value/
    input_field[/input/] = "input #{value.chomp}" unless input_field =~ /value/
    out << <<-HTML
  #{input_field}
</div>
HTML
    out.html_safe
  end
end

It supports everything as: :radio_buttons will accept with the addition of an optional 3rd argument for button styles.

= f.input :rad_buttons,
  as: :button_radio,
  collection: [ [:blue, 1, :primary],
    [:red, 2, :danger],
    [:green, 3, :success],
  ]

The haml snippet above will produce a three radio button group

  • first button styled to btn-primary with the label blue and the value 1
  • second button styled to btn-danger with the label red and the value 2
  • third button styled to btn-success with the label green and the value 3

Since we did not assign a value (input_html: {value: 1}) the last button will be made active (the same behaviour as for normal radio buttons) and the value of rad_buttons will be 3

nJoy!

Upvotes: 2

toashd
toashd

Reputation: 1002

You can also use a hidden input field with some unobtrusive JavaScript. Put the hidden field together with the twitter bootstrap toogle buttons in your *.html.erb:

<%= f.hidden_field :child_gender %>
<div id="gender-toggle" class="btn-group" data-toggle="buttons-radio">
  <button type="button" class="btn" data-gender="boy">Boy</button>
  <button type="button" class="btn" data-gender="girl">Girl</button>
</div>

In your *.js.coffee just set the active button and input value:

# Activate selected gender
$("button[data-gender=" + $('#hidden_field_id').val() + "]").addClass('active')

# Set gender value
$("#gender-toggle button").click -> 
  $('#hidden_field_id').val($(this).data('gender'))

Hope this helps.

Upvotes: 2

Related Questions