First, please excuse me if this question is dumb, I am just starting to wrap my head around Rails, and Javascript & jQuery are a whole new world for me.
I have found the following, similar questions, but simply don't understand how they could apply to my situation:
Show/hide div if checkbox selected
Check Hidden Input Fields Based On Radio Button Selected
Show form fields based on radio button selection
That being said, here is my question.
In my Rails 4 app, I have the following Rails form (I am NOT using Simple Form):
<div class="calendar_details">
<%= f.label :target_relationship %>
<%= radio_button_tag(:target_relationship, "B2C", :checked => true, :onclick=>"showMe('calendar_details_b2c')", {:class => "radio_button_target_relationship_b2C"}) %>
<%= label_tag(:target_relationship, "B2C") %>
<%= radio_button_tag(:target_relationship, "B2B", :onclick=>"showMe('calendar_details_b2b')", {:class => "radio_button_target_relationship_b2b"}) %>
<%= label_tag(:target_relationship, "B2B") %>
</div>
<div class="calendar_details">
<%= f.label :target_country %><%= f.country_select :target_country, ["United States"] %>
</div>
<div id="calendar_details_b2c">
<div class="calendar_details">
<%= f.label :target_gender %><%= radio_button_tag(:target_gender, "Female") %><%= label_tag(:target_relationship, "Female") %><%= radio_button_tag(:target_gender, "Male") %><%= label_tag(:target_relationship, "Male") %><%= radio_button_tag(:target_gender, "Both", :checked => true) %><%= label_tag(:target_relationship, "Both") %>
</div>
<div class="calendar_details">
<%= f.label :target_age_lower_limit %><%= f.select :target_age_lower_limit, (0..99) %>
</div>
<div class="calendar_details">
<%= f.label :target_age_upper_limit %><%= f.select :target_age_upper_limit, (0..99) %>
</div>
<div class="calendar_details">
<%= f.label :target_household_income_lower_limit %><%= f.select :target_household_income_lower_limit, ['Less than $10,000', '$10,000', '$20,000', '$30,000', '$40,000', '$50,000', '$60,000', '$70,000', '$80,000', '$90,000', '$100,000', '$110,000', '$120,000', '$130,000', '$140,000', '$150,000', '$160,000', '$170,000', '$180,000', '$190,000', '$190,000', '$200,000', 'More than $200,000'] %>
</div>
<div class="calendar_details">
<%= f.label :target_household_income_upper_limit %><%= f.select :target_household_income_upper_limit, ['Less than $10,000', '$10,000', '$20,000', '$30,000', '$40,000', '$50,000', '$60,000', '$70,000', '$80,000', '$90,000', '$100,000', '$110,000', '$120,000', '$130,000', '$140,000', '$150,000', '$160,000', '$170,000', '$180,000', '$190,000', '$190,000', '$200,000', 'More than $200,000'] %>
</div>
</div>
<div id="calendar_details_b2b">
<div class="calendar_details">
<%= f.label :target_company_size %><%= f.select :target_company_size, ['Self-employed', '1-10 employees', '11-50 employees', '51-200 employees', '201-500 employees', '501-1,000 employees', '1,001-5,000 employees', '5,001-10,000 employees', 'More than 10,000 employees'] %>
</div>
<div class="calendar_details">
<%= f.label :target_industry %><%= f.select :target_industry, ['Art & Entertainment', 'Autos & Vehicles', 'Beauty & Fitness', 'Books & Litterature', 'Business & Industrial', 'Computer & Electronics', 'Finance', 'Food & Drinks', 'Games', 'Hobbies & Leisure', 'Home & Garden', 'Internet & Telecom', 'Jobs & Education', 'Law & Government', 'News', 'Online Communities', 'People & Society', 'Pets & Animals', 'Real Estate', 'Science', 'Shopping', 'Sports', 'Travel'] %>
</div>
</div>
Based on what users check on the first radio button (either "B2C" or "B2B"), I would like to either display the calendar_details_b2c div, or the calendar_details_b2b div.
I understand I am going to need to hide both divs, then to implement some form of condition, checking which radio button is checked, and finally display the right div.
As you can see, I tried to add an onclick option and some specific classes to my radio buttons, but then I am stuck: I don't know how to build the correct js function, and I don't know where to include it (in the .html.erb file of the form, in the header of the app, in the application.js file?).
—————
UPDATE: as per Ziv Galili's answer, here is what I have now:
In app/assets/javascript/custom/calendars.js:
$(document).ready(function() {
$('input[type=radio][name=calendar').change(function () {
// first: hide all the divs
$('#calendar_details_b2c').css("display","none");
$('#calendar_details_b2b').css("display","none");
// then get the div ID to show (I stored it in the "value" of the radio button)
var fieldToShow = $(this).val();
// now use jQuery selector and change the display setting of that field
$("#" + fieldToShow).css("display","block");
});
});
In application.js, I added //= require_tree ./custom to take the above code into consideration in my app.
In the view where my form is (Calendars#New view), I now have:
<div class="calendar_details">
<%= f.label :target_relationship, "Business relationship" %>
<%= f.radio_button :target_relationship, "calendar_details_b2c", :checked => true %>
<%= f.label(:target_relationship, "B2C") %>
<%= f.radio_button :target_relationship, "calendar_details_b2b", :checked => false %>
<%= f.label(:target_relationship, "B2B") %>
</div>
<div class="calendar_details">
<%= f.label :target_country, "Country" %><%= f.country_select :target_country, ["United States"] %>
</div>
<div id="calendar_details_b2c">
<div class="calendar_details">
<%= f.label :target_gender, "Gender" %><%= radio_button_tag(:target_gender, "Female") %><%= label_tag(:target_relationship, "Female") %><%= radio_button_tag(:target_gender, "Male") %><%= label_tag(:target_relationship, "Male") %><%= radio_button_tag(:target_gender, "Both", :checked => true) %><%= label_tag(:target_relationship, "Both") %>
</div>
<div class="calendar_details">
<%= f.label :target_age_lower_limit, "Age / Lower limit" %><%= f.select :target_age_lower_limit, (0..99) %>
</div>
<div class="calendar_details">
<%= f.label :target_age_upper_limit, "Age / Upper limit" %><%= f.select :target_age_upper_limit, (0..99) %>
</div>
<div class="calendar_details">
<%= f.label :target_household_income_lower_limit, "Household income / Lower limit" %><%= f.select :target_household_income_lower_limit, ['Less than $10,000', '$10,000', '$20,000', '$30,000', '$40,000', '$50,000', '$60,000', '$70,000', '$80,000', '$90,000', '$100,000', '$110,000', '$120,000', '$130,000', '$140,000', '$150,000', '$160,000', '$170,000', '$180,000', '$190,000', '$190,000', '$200,000', 'More than $200,000'] %>
</div>
<div class="calendar_details">
<%= f.label :target_household_income_upper_limit, "Household income / Upper limit" %><%= f.select :target_household_income_upper_limit, ['Less than $10,000', '$10,000', '$20,000', '$30,000', '$40,000', '$50,000', '$60,000', '$70,000', '$80,000', '$90,000', '$100,000', '$110,000', '$120,000', '$130,000', '$140,000', '$150,000', '$160,000', '$170,000', '$180,000', '$190,000', '$190,000', '$200,000', 'More than $200,000'] %>
</div>
</div>
<div id="calendar_details_b2b">
<div class="calendar_details">
<%= f.label :target_company_size, "Company size" %><%= f.select :target_company_size, ['Self-employed', '1-10 employees', '11-50 employees', '51-200 employees', '201-500 employees', '501-1,000 employees', '1,001-5,000 employees', '5,001-10,000 employees', 'More than 10,000 employees'] %>
</div>
<div class="calendar_details">
<%= f.label :target_industry, "Industry" %><%= f.select :target_industry, ['Art & Entertainment', 'Autos & Vehicles', 'Beauty & Fitness', 'Books & Litterature', 'Business & Industrial', 'Computer & Electronics', 'Finance', 'Food & Drinks', 'Games', 'Hobbies & Leisure', 'Home & Garden', 'Internet & Telecom', 'Jobs & Education', 'Law & Government', 'News', 'Online Communities', 'People & Society', 'Pets & Animals', 'Real Estate', 'Science', 'Shopping', 'Sports', 'Travel'] %>
</div>
</div>
However, I can't seem to make this work: when I visit the Calendars#New view, I do see the radio buttons to select either B2C or B2B, but whichever button I select, nothing is displayed below (neither the B2C section nor the B2B section).
What am I missing?
—————
UPDATE 2: So, I updated my code as Ziv Galili's new comment, ie: paying attention to the name of the button group, which is actually calendar[target_relationship].
When I did that, and tried to go to my view, I got a execJS::RuntimeError, which made me realize we were using pure JavaScript, while my Rails app seems to be using CoffeeScript.
So, I deleted app/assets/javascript/custom/calendars.js, converted Ziv Galili's code to CoffeeScript and added it to app/assets/javascript/calendars.coffee:
$('input[type=radio][name=calendar[target_relationship]]').change ->
# first: hide all the divs
$('#calendar_details_b2c').css 'display', 'none'
$('#calendar_details_b2b').css 'display', 'none'
# then get the div ID to show (i stored it in the "value" of the radio button
fieldToShow = $(this).val()
# now use jQuery selector and change the display setting of that field
$('#' + fieldToShow).css 'display', 'block'
return
I also replaced //= require_tree ./custom with //= require_tree . to make sure all my .coffee files were loaded through application.js.
Despite all these code updates, I still do not get the result I am expecting: none of the divs are displayed in my views:
I must be missing something really, really obvious, but I can't figure out what it is.
Any idea?
—————
Any kind of help would be highly appreciated.
i would do something like this:
jsFiddle
Explanation:
lets say the HTML will be:
<form action="">
<input type="radio" name="calendars" value="calendar_details_b2b">B2B
<br>
<input type="radio" name="calendars" value="calendar_details_b2c">B2C
</form>
<div id="calendar_details_b2c" style=>
content of B2C
</div>
<div id="calendar_details_b2b">
content of B2B
</div>
add css code so the divs won't be shown on page load:
#calendar_details_b2c,
#calendar_details_b2b
{
display: none;
}
JS code will be:
$('input[type=radio][name=calendars]').change(function () {
// first: hide all the divs
$('#calendar_details_b2c').css("display","none");
$('#calendar_details_b2b').css("display","none");
// then get the div ID to show (i stored it in the "value" of the radio button
var fieldToShow = $(this).val();
// now use jQuery selector and change the display setting of that field
$("#" + fieldToShow).css("display","block");
});
UPDATE:
as for your update, please pay attention that in the JS section the name of the button group match their name in the HTML file:
js:
$('input[type=radio][name=NAME_OF_BUTTON_GROUP]')
to find the name in the HTML you can (in chrome):
Right click on one of the radio buttons
press Inspect Element
in the element inspector look for the name attribute of the input
<input checked="checked" id="something_calendar_details_b2c" name="THIS IS THE NAME" type="radio" value="calendar_details_b2c">
NOTE: if you have square brackets in the name (or any other special character) you should wrap it with apostrophes
$('input[type=radio][name="calendar[target_relationship]"]')
You can easily use jQuery functions in js file.
just use
$('input[type=radio][name=abc]').change(function() {
// check radio button value by this.value
// hide div blocks by $('div#id').hide();
});
<script src="//code.jquery.com/jquery-1.11.1.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js"></script>
<script>
$(function() {
var $divs = $('#divs > div');
$divs.first().show();
$('input[type=radio]').on('change',function() {
$divs.hide();
$divs.eq( $('input[type=radio]').index( this ) ).show();
});
});
</script>
#divs div {
display:none;
}
<div class="container">
<div class="form-container">
<form class="registerform" action="register.php" method="post" id="registerform">
<h3 class="title">Register</h3>
<div class="cdiv">User Name:</div>
<div class="cdiv"><input class="ipcontact" name="username" type="text" id="username"></div>
<div class="cdiv">Password:</div>
<div class="cdiv"><input class="ipcontact" name="password" type="password" id="password"></div>
<div class="cdiv">Name:</div>
<div class="cdiv"><input class="ipcontact" name="name" type="text" id="name"></div>
<div class="cdiv">E-mail:</div>
<div class="cdiv"><input class="ipcontact" name="email" type="email" id="email"></div>
<div class="cdiv">Phone Number:</div>
<div class="cdiv"><input class="ipcontact" name="phonenumber" type="number" id="phonenumber"></div>
<div class="cdiv">Student<input type="radio" name="check" checked value="1">Teacher<input type="radio" name="check" value="2"></div>
<div id = "divs">
<div class="form-group" id = "div2">
Student Class:<br/>
<input class="ipcontact" name="studentclass" type="text" id="studentclass"><br/>
Student Roll Number:<br/>
<input class="ipcontact" name="studentrollnumber" type="text" id="studentrollnumber">
</div>
<div class="form-group" id = "div1" >
Teacher type:<br/>
<input class="ipcontact" name="teachertype" type="text" id="teachertype">
</div>
</div>
<div class="form-group"> <input class="login-button" type="submit" name="submit" value="Register"></div>
</form>
</div>
</div>
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.1.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js"> </script>
</head>
<body>
<script>
$(function() {
var $divs = $('#divs > div');
$divs.first().show();
$('input[type=radio]').on('change',function() {
$divs.hide();
$divs.eq( $('input[type=radio]').index( this ) ).show();
});
});
</script>
<div class="container">
<div class="form-container">
<form class="registerform" action="register.php" method="post" id="registerform">
<h3 class="title">Register</h3>
<div class="cdiv">User Name:</div>
<div class="cdiv"><input class="ipcontact" name="username" type="text" id="username"></div>
<div class="cdiv">Password:</div>
<div class="cdiv"><input class="ipcontact" name="password" type="password" id="password"></div>
<div class="cdiv">Name:</div>
<div class="cdiv"><input class="ipcontact" name="name" type="text" id="name"></div>
<div class="cdiv">E-mail:</div>
<div class="cdiv"><input class="ipcontact" name="email" type="email" id="email"></div>
<div class="cdiv">Phone Number:</div>
<div class="cdiv"><input class="ipcontact" name="phonenumber" type="number" id="phonenumber"></div>
<div class="cdiv">Student<input type="radio" name="check" checked value="1">Teacher<input type="radio" name="check" value="2"></div>
<div id = "divs">
<div class="form-group" id = "div2">
Student Class:<br/>
<input class="ipcontact" name="studentclass" type="text" id="studentclass"><br/>
Student Roll Number:<br/>
<input class="ipcontact" name="studentrollnumber" type="text" id="studentrollnumber">
</div>
<div class="form-group" id = "div1" >
Teacher type:<br/>
<input class="ipcontact" name="teachertype" type="text" id="teachertype">
</div>
</div>
<div class="form-group"> <input class="login-button" type="submit" name="submit" value="Register"></div>
</form>
</div>
</div>
</body>
</html>
In an attempt to make my rails html more readable I extracted several parts of it into partials. I then use jquery to render the partials. The issue is that now the form has come all "unhooked" so to speak, meaning when I attempt to submit the form it acts as though the partials don't exist. I suspect I am not understanding quite how forms work, because it seems like in other answers related to this the form builder isn't even addressed.
This SO question seems related to what I want to do but I think I'm too inexperienced to grasp it properly
The code I have thus far goes as follows:
/assets/javascripts/work_order.js
$(document).ready(function(){
$('.best_in_place').best_in_place();
$('#work_order_dueDate').datepicker();
$.datepicker.setDefaults({ dateFormat: 'dd-mm-yy'});
var selection_made = false
$('#work_order_project_type_id').change(function(){
if (!selection_made){
selection_made = true
var selection = $(this).find('option:selected').text();
if (selection == "Variable Data Mailing"){
$.get('/presort_informations/new');
$.get('/printing_instructions/new');
}
else if (selection == "Mailing"){
$.get('/presort_informations/new');
}
else if (selection == "Print Job"){
$.get('/printing_instructions/new');
}
}
});
});
and then
/views/work_orders/_form.html.erb
<%= form_for(#workorder) do |f| %>
<% if #workorder.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#workorder.errors.count, "error") %> prohibited this workorder from being saved:</h2>
<ul>
<% #workorder.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<fieldset class="general-info">
<legend>General</legend>
<div class="col-md-12">
<div class="col-md-3">
<div class="form-group">
<%= f.label :Job_Title, class: "control-label" %>
<%= f.text_field :title, class:"form-control" %>
</div>
<div class="form-group">
<%= f.label :Project_Type, class: "control-label" %>
<%= f.collection_select(:project_type_id, ProjectType.all, :id, :name, {:prompt => true}, {:class => "form-control"}) %>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<%= f.label :Rep, class: "control-label" %>
<%= f.text_field :rep, class:"form-control" %>
</div>
<div class="form-group">
<%= f.label :Labels, class: "control-label" %>
<%= f.collection_select(:labels_id, Labels.all, :id, :name, {:prompt => true}, {:class => "form-control"}) %>
</div>
</div>
<div class="col-md-3">
<div class= "form-group">
<%= f.label :Due_Date, class: "control-label" %>
<%= f.text_field :dueDate, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :Project_Description, class: "control-label" %>
<%= f.text_area :projectDescription, class: "form-control" %>
</div>
</div>
</div>
</fieldset>
<fieldset class="presort-information">
</fieldset>
<div class="col-md-6 printing">
</div>
<fieldset class="production-details">
<legend>Production</legend>
<%= f.fields_for :production_details, ProductionDetails.new do |ff| %>
<%end%>
</fieldset>
<%= f.hidden_field(:number, :value => #workorder.number) %>
<%= f.hidden_field(:client_id, :value => #workorder.client_id) %>
<%= f.submit(class: "btn btn-default") %>
<% end %>
and as an example of one of the partials:
/app/views/presort_informations/new.js.erb
$('.presort-information').append( '<%= j render("presort_informations/form") %>' );
/app/views/presort_informations/_form.html.erb
<legend>Mailing</legend>
<%= fields_for :presort_information, PresortInformation.new do |ff| %>
.
.
.
<% end %>
I'm not really sure how to tie this all together so that I can load the partials based on the select box, but then submit them all as one form.
Edit:
I found this SO question which deals with the same issue, but I suspect that because I am rendering the partial after the page has been loaded I no longer have access to the form builder variable.
$('.presort-information').append( '<%= j render("presort_informations/form", f: f) %>' );
gives an undefined variable error when it's called. I'm still not sure how to bridge this gap between jquery and rails.
Turns out it was a relatively (if new conceptually to me) easy fix
First, load each DOM partial in along with hidden sections.
<%= form_for(#workorder) do |f| %>
<% if #workorder.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#workorder.errors.count, "error") %> prohibited this workorder from being saved:</h2>
<ul>
<% #workorder.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<fieldset id="general-info-partial">
<%=render("genInfo", f: f)%>
</fieldset>
<fieldset id="presort-information-partial">
<%=render("presort_informations/form", f: f)%>
</fieldset>
<div class="col-md-6">
<fieldset id="printing-information-partial">
<%=render("printing_instructions/form", f: f)%>
</fieldset>
</div>
<fieldset id="production-details-partial">
<%=render("production_details/form", f: f) %>
</fieldset>
<%= f.hidden_field(:number, :value => #workorder.number) %>
<%= f.hidden_field(:client_id, :value => #workorder.client_id) %>
<input type="submit" value="Submit" class="btn btn-default">
<% end %>
<div id="hidden-general-info" class="hidden"></div>
<div id="hidden-presort-information" class="hidden"></div>
<div id="hidden-printing-information" class="hidden"></div>
Then the Javascript to move things in and out of the form:
$(document).ready(function(){
$('.best_in_place').best_in_place();
$('#work_order_dueDate').datepicker();
$.datepicker.setDefaults({ dateFormat: 'dd-mm-yy'});
var presortFields = $('#presort-information-partial');
var printingFields = $('#printing-information-partial');
var presortHidden = $('#hidden-presort-information');
var printingHidden = $('#hidden-printing-information');
presortHidden.html(presortFields.html());
presortFields.html('');
printingHidden.html(printingFields.html());
printingFields.html('');
$('#work_order_project_type_id').change(function(){
var selection = $(this).find('option:selected').text();
if (selection == "Variable Data Mailing"){
if (printingFields.html() == '' && presortFields.html() == ''){
printingFields.html(printingHidden.html()).hide().slideDown();
presortFields.html(presortHidden.html()).hide().slideDown();
}
else if(printingFields.html() == '' && !(presortFields.html() == '')){
printingFields.html(printingHidden.html()).hide().slideDown();
}
else if(!(printingFields.html() == '') && presortFields.html() == ''){
presortFields.html(presortHidden.html()).hide().slideDown();
}
}
else if (selection == "Mailing"){
if(!(printingFields.html() == '')){
printingFields.slideUp();
printingFields.html('');
presortFields.html(presortHidden.html()).hide().slideDown();
}else{
presortFields.html(presortHidden.html()).hide().slideDown();
}
}
else if (selection == "Print Job"){
printingFields.html(printingHidden.html()).hide().slideDown();
presortFields.slideUp();
presortFields.html('');
}
});
Basically, the idea was to load everything in as if I was going to use it all, and then just move the partials into a hidden section of the DOM, and then use JS to put them back in when the user makes a selection