I am trying to make an app in Rails 4.
I use simple form for the form and have a js form helper file which I use to have javascript hide or show form elements depending on how users answer questions as they move through the form.
My problem is that this js function works fine on the initial create, but if i come back to edit the form, the reminder id content is shown when the draft attribute is == false.
Is there another way to approach this problem?
My form has:
<%= f.simple_fields_for :scope do |finalises_s| %>
<%= finalises_s.simple_fields_for :finalise do |dr| %>
<div class="row">
<div class="col-md-3 col-md-offset-1">
<%= dr.label :draft, 'Would you like to save this draft and finish it later?', class: 'question-project' %>
</div>
<div class="col-md-7">
<%= dr.collection_radio_buttons :draft, [[true, ' Yes, save a draft'], [false, ' No, publish it']], :first, :last, {:item_wrapper_class => 'fixradio'}, {:class => "response-project"} %>
</div>
</div>
<div id="reminder">
<div class="row">
<div class="col-md-3 col-md-offset-1">
<%= dr.label :reminder, 'Would you like a reminder to complete this draft?', class: 'question-project' %>
</div>
<div class="col-md-7">
<%= dr.collection_radio_buttons :reminder, [[true, ' Yes'], [false, ' No']], :first, :last, {:item_wrapper_class => 'fixradio'}, {:class => "response-project"} %>
</div>
</div>
My js form-helper file has:
if $("input:radio[name='"+ inString + "[scope_attributes][finalise_attributes][draft]']").is(':checked')
$('#reminder').show()
else
$('#reminder').hide()
$(document).on 'change', "input:radio[name='"+ inString + "[scope_attributes][finalise_attributes][draft]']", ()->
if $(this).val() == 'true'
$('#reminder').show()
else
$('#reminder').hide()
There are probably a few different ways to do this, but I did something very similar.
Attach an id or class to the radio button you can use to bind later (in my case, for each radio/value combination, I added a unique class like class: this_radio_value_true).
Similarly, attach an id or class to the sections of markup you want to impact (in my case, I added an id to a form_group which I wanted to hide/show like id: stuff_to_control).
Trigger jQuery for these elements:
$('.this_radio_value_true').on('click', function() {
$('#stuff_to_control').show();
});
$('.this_radio_value_false').on('click', function() {
$('#stuff_to_control').hide();
});
It's probably because TurboLinks. You can't use $(document).on 'change'. You have to listing for TurboLink events. Rails doesn't load the entire page each time. It only loads the content inside the layout. You have to use
$(document).on 'ready page:load', ->
Here's the docs: https://github.com/rails/turbolinks/#events
Guide: http://guides.rubyonrails.org/working_with_javascript_in_rails.html#page-change-events
Related
I have not been able to figure out if this is a simple_fields_for problem, a cocoon issue, or something else. If I have blundered with a </div> placement, I don't see it.
When the form first displays, it renders an input field for protocol name. The user can click buttons to add form elements of interest. This works fine and looks like this:
Here's how it looks after the user has clicked on each of the "Add an element" buttons:
The user can add 0..many of each of the elements. When they click on 'Save' it all works really well.
If there is a validation error in one of the fields, the form re-renders fine with one exception. Validation errors for the "Imaging Step" elements do not re-display at all. The other elements re-render and are highlighted as expected when validation fails.
Here's a pictorial example. The user fills out part of the form, having forgotten to select a "Sequence" and having forgotten to enter text for "Tip Description":
After clicking 'save' and failing validation, the form re-renders like this:
As you can see, the Imaging Step section has not been redrawn.
If I look at params in the context of the view, everything seems to be there. #protocol.errors looks right to me as well. Models seem OK too.
Here is a pastebin of the form code.
Here is a pastebin of _step_item_fields.html.erb.
Here is a pastebin of _tip_fields.html.erb.
Here is a pastebin of my Gemfile.
UPDATE:
if I build a step_item like this:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
The Imaging Step section is always drawn, but (obviously) does not populate when validation fails. This also confuses the feature of letting the user add/remove 0..many Imaging Steps.
I also tried:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(protocol_params) do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
... and other variants when protocol_params is available, but ran in to forbidden params issues.
UPDATE2:
I also tried building a hash with params for a step_item. I can use this here:
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(some_ok_params) do |si| %>
<%= render 'step_item_fields', :f => si %>
<%end%>
</div>
... but only for a single step_item. Am not sure how to pass a hash of hashes reflecting the 0..many functionality. Also, building a step_item this way populates the form element correctly, but does not include the error highlighting styling. This is about the time I started wondering why the simpler solution was not working.
Since StepItem is a sub-type of the STI class Step, I think the error detection for StepItems was getting missed. My hack fix here is not pretty and admittedly brittle. Please post a better way if you have one.
I changed this in _form.html.erb:
...
<%if params["protocol"] && params["protocol"]["step_items_attributes"].present?%>
<%params["protocol"]["step_items_attributes"].each do |sia|%>
<%v = sia[1]%>
<div id="step_items">
<%= f.simple_fields_for :step_items, #protocol.step_items.build(:orientation_id => v["orientation_id"], :sequence_id => v["sequence_id"], :note => v["note"], :id => v["id"], :protocol_id => v["protocol_id"], :institution_id => v["institution_id"] ) do |si| %>
<%= render 'step_item_fields', :f => si%>
<% end %>
</div><!-- end step_items-->
<%end%>
<%else%>
...
I changed this in _step_item_fields.html.erb:
...
<div class="nested-fields">
<div class= "form-inputs">
<div class="row">
<div class="col-sm-2 text-right">Imaging Step:</div>
<div class="col-sm-2 drop_col ">
<%= f.collection_select :orientation_id, Orientation.where("institution_id=?",current_user.current_institution_id),:id,:name, {:prompt => "Pick an Orientation", }, {class: "form-control #{"dropdown_error" if f.object.orientation_id.blank? && (f.object.sequence_id.present? || f.object.note.present?)}"}%>
</div>
<div class="col-sm-2 drop_col ">
<%= f.collection_select :sequence_id, Sequence.where("institution_id=?",current_user.current_institution_id),:id,:name, {:prompt => "Pick a Sequence"}, {class: "form-control #{ "dropdown_error" if f.object.sequence_id.blank? && (f.object.orientation_id.present? || f.object.note.present?) }"}%>
</div>
</div>
<div class="row">
<div class="col-sm-2"></div>
<%= f.input :note, :wrapper_html =>{:class => 'col-sm-8'}, label: false, placeholder: 'You can put an optional note here.' , :input_html => {:size => 50}%>
<%= f.input :institution_id,:as => :hidden, :input_html => {:value=>current_user.current_institution_id} %>
<%= f.input :id,:as => :hidden %>
<div class="col-sm-2">
<%= link_to_remove_association "Remove Imaging Step", f , :class=>"btn btn-danger btn-xs placemid",title: "Click here to delete this imaging step.", data: {toggle: "tooltip", placement: "auto", animation: true, delay: {show: 700, hide: 100}}%>
</div>
</div>
</div>
<div class="col-sm-12"><hr></div>
</div>
...
And I added this to the correct stylesheet:
.dropdown_error{
color: red;
}
The form now displays StepItem errors correctly as seen here:
There is probably a simple solution for this, however, I couldn't find it.
I have a simple form where I have state select box & site text_field:
_form.html.erb
<div class="field">
<%= f.label :state, 'State' %><br>
<%= f.select :state, [['NSW'], ['VIC'], ['SA'], ['WA'], ['QLD']] %>
</div>
<div class="form-group">
<%= f.label :site, 'Site'%><br>
<%= f.text_field :site %>
</div>
I would really like to be able to select "VIC" (a state in Australia) and then due to the fact that "VIC" was selected, the :site text_field is automatically populated with another value such as a site name.
For example: "VIC" state is selected -> "Melbourne" site is populated in the text_field on the form (in :site)
I have played around a bit with some example javascript that I have found on the web which seems to update some text on the screen when a certain value is chosen in a select box, however, I cannot seem to form an "if" argument that states: if "VIC" selected, then populate text_field of :site with "Melbourne".
Thanks in advance.
code for javascript that changes to the value selected:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<div id="text_to_be_changed"><%= #day_of_week %></div>
<%= select_tag :days, options_for_select(["Monday", "Tuesday"]), :id => "select_days" %>
<script>
$(document).ready(function(){
$('#select_days').change(function() {
$('#text_to_be_changed').html($(this).val());
});
});
</script>
You can try this:
<script>
$(document).ready(function(){
$('#select_days').change(function() {
$('#text_to_be_changed').html($(this).val());
});
// adding pre-populate data
$("#configfile_state").change(function(){
var selected = $(this).val();
if(selected == "VIC") {
$("#configfile_site").val("Melbourne");
}
});
});
</script>
I hope this help you.
You will need the javascript to "know" the relationship between "VIC" and "Melbourne", or more generally between states and their capital cities. This can be stored in a variety of ways, eg as an array or map in your js code. eg
html:
<div class="field">
<%= f.label :state, 'State' %><br>
<%= f.select :state, [['NSW'], ['VIC'], ['SA'], ['WA'], ['QLD']], :id => "state-select" %>
</div>
<div class="form-group">
<%= f.label :site, 'Site'%><br>
<%= f.text_field :site, :id => "city-select" %>
</div>
js:
$(document).ready(function(){
var stateDefaultCities = {
"VIC": "Melbourne",
"NSW": "Sydney",
"WA": "Perth"
//etc
}
$('#state-select').change(function() {
$('#city-select').val(stateDefaultCities[$(this).val]);
});
});
EDIT: this solution is a bit fragile since the states are repeated in the markup and html: eg if you were to add an option to the select you'd need to add a corresponding line to the stateDefaultCities object. But it's just an example.
I dint even understand how to search for my problem scenario in google.
Problem : When a user selects from a drop-down, the below form fields should change accordingly.
Example : If a user selects "Human" from Drop-down , then below form_fields should be shown like "age" field ,"ethnicity" field etc..
If a user selects "Event" from drop-down, below form fields should show "Date", "venue" etc..
and By default we will have "SUBMIT" button.
Can anyone tell how to achieve this ? Any help with simple_form ruby gem also would be helpful.
You can do this with some simple javascript. Lets say you have a form with the following fields
<%= f.select :some_field, %w[Human Cat], {}, {:class => 'my-select'} %>
<div class="human">
<%= f.label :age %><br>
<%= f.text_field :age %>
</div>
<div class="cat" style="display:none;">
<%= f.label :sub_species %><br>
<%= f.text_field :sub_species %>
</div>
Now add some javascript to make it dynamic
<script>
$(".my-select").change(function(){
if($(".my-select").val() == "Human"){
$(".human").css('display', 'block');
$(".cat").css('display', 'none');
}else{
$(".human").css('display','none');
$(".cat").css('display', 'block');
}
})
</script>
This is quite easy to achieve with the simple_form gem and some simple javascript.
View
<%= simple_form_for #object do |f| %>
<%= f.input :attribute_name, :collection => ['Human', 'Event'], :input_html => { :class => 'dropdown' %>
<div class="toggle human" style="display:none;">
<%= f.input :age %>
</div>
<div class="toggle event" style="display:none;">
<%= f.input :date %>
</div>
<%= f.button :submit %>
<% end %>
The key here is wrapping the fields you want to toggle in div with specific selectors.
Then we look for a change event on the dropdown field, and toggle our divs accordingly.
Javascript
$('.dropdown').change(function() {
// hide divs first
$('form .toggle').hide();
// get value from dropdown
var divClass = $('.dropdown').val().toLowerCase();
// show necessary div
$('.' + divClass).show();
});
Using this approach, you don't need to write the specific case for every value of the dropdown, but can use the lower case value of the dropdown as the selector for the div.
Here is a simple example with static markup.
I have a a partial, _form for one of my views in rails. It looks like so:
<%= form_for(#project) do |f| %>
<div class="field">
<%= f.label "Project Status" %><br />
<%= f.select :status, ["In Progress", "Cancelled"] %>
</div>
<div class="field">
<%= f.label "Capital Cost" %><br />
<%= f.text_field :capital_cost %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I'd like the "capital cost" part of the form to be greyed out unless "In Progress" is selected from the dropdown menu, without having to reload the page. Is this possible? I saw some solutions using javascript, but I'm a complete beginner and couldn't get my head around them (unfortunately I don't have the time to learn to use js before I have to finish this project). Cheers!
For this you need some JavaScript. Use an onchange event handler to monitor the <select> input for changes. Compare the input value and conditionally set/unset a disabled attribute on the #project_capital_cost input. You can use jQuery for this.
By default, Rails 3 enables jQuery by including the jquery_rails gem in your Gemfile. Assuming you have jquery_rails included in your app and your <select> and <input> tags have #project_status and #project_capital_cost IDs respectively, add the following script into your _form partial with necessary modification:
<script>
$(document).ready(function(){
if($('#project_status').val() != "In Progress"){
$("#project_capital_cost").attr('disabled','disabled');
}
else{
$("#project_capital_cost").removeAttr('disabled');
}
$('#project_status').change(function(){
if($(this).val() != "In Progress"){
$("#project_capital_cost").attr('disabled','disabled');
}
else{
$("#project_capital_cost").removeAttr('disabled');
}
})
});
</script>
EDIT:
To hide div give some id to it:
<div class="field" id="my_div">
<%= f.label "Capital Cost" %><br />
<%= f.text_field :capital_cost %>
</div>
Then replace
$("#project_capital_cost").attr('disabled','disabled'); with $("#my_div").css('display','none')
and
$("#project_capital_cost").removeAttr('disabled'); with $("#my_div").css('display','block') in script.
To grey out the input, use the HTML input tag attribute disabled.
<input disabled="disabled">
Which from Rails is
<%= f.text_field :capital_cost, :disabled => "disabled", :id => "capital_cost_input" %>
To enable it when "In Progress" is selected will require AJAX and Javascript, but there is some help from Rails.
First
<%= f.select :status, ["In Progress", "Cancelled"],
{},
"data-remote" => true, "data-url" => ..._path %>
This will set the onchange attribute for you and make the AJAX call.
Add a route to handle the AJAX call, and supply the URL for the "data-url".
In the view template for the AJAX action, write the Javascript to enable the input.
Something like
$('#capital_cost_input').removeAttr("disabled")
I am trying to load two spinners on two fields, in the same (rails) form, that both have a keyup remote call.
Using Jquery, I have the following code:
<div class="form_element">
<%= f.label :email, :class=>"field_hint", :title=>"Email" %>
<%= f.text_field :email %>
<div class="myloader" style="display:none;">
<p>
validating...
</p>
</div>
</div>
<div class="form_element">
<%= f.label :username, :class=>"field_hint", :title=>"Username" %>
<%= f.text_field :username %>
<div class="myloader" style="display:none;">
<p>
validating...
</p>
</div>
</div>
Both of these fields have a keyup that does a remote call:
$('.form_element').ajaxStart(function() {
$(this).children('.myloader').show();
});
$('.form_element').ajaxStop(function() {
$(this).children('.myloader').delay(1000).fadeOut('fast');
});
At the moment when an the ajax happens both spinners appear when I only want the spinner who is a sibling of the field, I am trying to work out how to make each spinner div relevant to its field.
I am new to Ajax so any advice on maybe doing the ajaxStart/Stop differently are more than welcome, maybe a way to only have one spinner which moves next to the field instead of having two would be helpful.
I encourage you to use this jQuery plugin for your task: http://plugins.jquery.com/project/nimbleLoader
It will allow you to create loading bar for any element in your web page. Here you can see example: http://www.salesclic.com/jquery.nimble.loader/demo/