简体   繁体   中英

How to dynamically show/hide Rails form fields using CoffeeScript and JQuery?

Trying to build a form that submits to multiple models based on a user-selected value.

I thought the Railscast episode for "#196: Nested Model Form" ( http://railscasts.com/episodes/196-nested-model-form-revised ) could help, but the solution wasn't exactly what I was looking for. I created multiple models with associations between them ( belongs_to and has_many ) and included the accepts_nested_attributes_for declaration to support the objects the form will generate:

class User < ActiveRecord::Base
  attr_accessible :password_digest, :uname, :user_type, :artist_attributes, :sponsor_attributes
  has_many :artists
  has_many :sponsors
  accepts_nested_attributes_for :artists, :sponsors

  validates :uname, presence: true, uniqueness: true
  has_secure_password
end

class Artist < ActiveRecord::Base
  attr_accessible :user_id, :address, :email, :genre, :name, :phone, :photo, :preferences, :rate, :terms, :artist_type
  belongs_to :user

  validates :email, :genre, :name, :phone, :photo, :preferences, :pwd, :terms, :artist_type, :uname, presence: true
  validates :rate, numericality: {greater_than_or_equal_to: 0}
  validates :phone, numericality: true  
  validates_length_of :phone, :minimum => 10, :message => "Phone numbers need to be at least 10 digits"
  validates :email, uniqueness: true
end

class Sponsor < ActiveRecord::Base
  attr_accessible :user_id, :address, :email, :name, :phone
  has_many :venues
  belongs_to :user

  validates :email, :name, :phone, presence: true
  validates :email, uniqueness: true
  validates :phone, numericality: true
  validates_length_of :phone, :minimum => 10, :message => "Phone numbers need to be at least 10 digits"
end

I'm using form_for to map to the User model and fields_for to map to the Sponsor and Artist models in the Users form partial (rendered in the Users#new view):

<%= form_for(@user) do |f| %>
  # Error handling omitted 
  <div class="field">
    <%= f.label :uname, "User Name" %><br />
    <%= f.text_field :uname %>
  </div>
  <div class="field">
    <%= f.label :pwd, "Password" %><br />
    <%= f.password_field :pwd, size: 30 %>
  </div>
  <div class="field">
    <%= f.label :pwd, "Confirm Password" %><br />
    <%= f.password_field :pwd, size: 30 %>
  </div>

  <!-- Want to use these radio buttons to dynamically display/hide the fields for the Sponsor and Artist models -->
  <div class="field">
    <%= f.label :user_type, "Register as: " %><br />
    <%= f.radio_button :user_type, "Sponsor", class: "sponsor_fields" %><%= f.label :user_type, "Sponsor", value: "Sponsor" %>
    <%= f.radio_button :user_type, "Artist", class: "artist_fields" %><%= f.label :user_type, "Artist", value: "Artist" %>
  </div>
  <!-- End dynamic display radio buttons --> 

  <div id="sponsor_fields" style="display:none;">
    <% f.fields_for :sponsor do |s| %>
    <div class="field">
      <%= s.label :name %><br />
      <%= s.text_field :name %>
    </div>
    <div class="field">
      <%= s.label :email %><br />
      <%= s.text_field :email %>
    </div>
    <div class="field">
      <%= s.label :phone %><br />
      <%= s.text_field :phone %>
    </div>
    <div class="field">
      <%= s.label :address %><br />
      <%= s.text_area :address %>
    </div>
    <% end %>
  </div>
  <div id="artist_fields" style="display:none;">
    <% f.fields_for :artist do |a| %>
    <div class="field">
      <%= a.label :name %><br />
      <%= a.text_field :name %>
    </div>
    <div class="field">
      <%= a.label :email %><br />
      <%= a.text_field :email %>
    </div>
    <div class="field">
      <%= a.label :phone %><br />
      <%= a.text_field :phone %>
    </div>
    <div class="field">
      <%= a.label :address %><br />
      <%= a.text_area :address %>
    </div>
    <div class="field">
      <%= a.label :artist_type %><br />
      <%= a.text_field :artist_type %>
    </div>
    <div class="field">
      <%= a.label :rate %><br />
      <%= a.number_field :rate %>
    </div>
    <div class="field">
      <%= a.label :terms %><br />
      <%= a.text_field :terms %>
    </div>
    <div class="field">
      <%= a.label :genre %><br />
      <%= a.text_area :genre %>
    </div>
    <div class="field">
      <%= a.label :preferences %><br />
      <%= a.text_area :preferences %>
    </div>
    <div class="field">
      <%= a.label :photo %><br />
      <%= a.text_field :photo %>
    </div>
  <% end %>
  </div>

  <div class="actions">
  <%= f.submit %>
  </div>
<% end %>

And here's the Coffeescript and Jquery code (NOTE: I'm NO Jquery expert, so I'm sure this syntax has major issues) that I'm trying to use to display the <DIV> blocks with the ID attribute that contains the value of the radio button element:

jQuery ->
    $('form').on 'change', 'input[name=user_type]', (event) ->
        curr = $(this).value.toLowerCase()
        alt = $(this).closest('input[name=user_type]').value.toLowerCase()
        $('#'+ alt +'_fields').hide()
        $('#'+ curr +'_fields').show()

When I click on the radio buttons, nothing happens. I thought it might be related to the display:none attributes in each <DIV> , so I removed them and realized the fields_for form elements aren't displayed at all.

Can two fields_for blocks be included in the same form partial? How should this be coded to make the form elements display? I want the Sponsor and Artist form elements to be hidden by default, but display when the appropriate radio button is clicked.

I'm new to Ruby on Rails, Jquery and Coffeescript, so any help is appreciated. FYI: I installed Rails 3.2.13 using Railsinstaller (Ruby version 1.9.3) and verified that Coffeescript version 1.6.3 is installed. Any ideas?

UPDATE Tracked down the issue with the help of Bartosz (thank you). I viewed the source of the HTML rendered in the browser and found the value of the name attribute used in the jQuery selector was incorrect. Instead of $('input[name=user_type]') , the selector should have included the controller name as $('input[name="user[user_type]"]') . I modified Bartosz's cleaner Coffeescript code to achieve the desired show/hide functionality with the correctly named selector:

jQuery ->
    $('input[name="user[user_type]"]').on 'click', (event) ->
        curr = event.target.value.toLowerCase()
        $('[id$=_fields]').hide()
        $('#'+curr+'_fields').show()

Hope this helps someone else looking for answers like I was...

Is the callback being triggered at all?

Try this:

$('input[name=user_type]').change (e) ->
    curr = e.target.value.toLowerCase()
    $('[id$=_fields]').hide()
    $('#'+ curr +'_fields').show()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM