2

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...

2
  • The simplies way is to use gem coocon github.com/nathanvda/cocoon it implements all the things you want. Commented Jan 1, 2014 at 22:21
  • No, everything under the "jQuery ->" line should be indented, too. I'll fix that in the original post... Commented Jan 4, 2014 at 17:37

1 Answer 1

1

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()
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for the suggested Coffeescript syntax, Bartosz (much cleaner & you taught me something new with the selector wildcard). I replaced the code after the jQuery -> line in my users.js.coffee file with your suggested code, but it still didn't work. It looks like the callback's not being triggered.
Try some debugging with breakpoints or console.log(e). Check if the input exists console.log $('input[name=user_type]'). I can't see why this wouldn't work.
I added a script block directly to \app\views\users_form.html.erb to see if that would make a difference, but it didn't work either (not even the alert): $(document).ready(function(){ $('input[name=user_type]').click(function(){alert(this.value); $('[id$=_fields]').hide(); $('#'+ this.value.toLowerCase() +'_fields').show(); }); });
Sorry for the rookie question, but where do I insert the console.log(e) call? In the coffeescript?
within change (e) -> function block. But I guess it'll show nothing if the alert failed to show. Check JS console for errors or show us the repo.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.