Send f.select parameter to jquery and show dynamic form elements - javascript

I have a form who uses a nasted form. I want to him show up when the f.select selects the option unificação.
I using a example that i see in a example, when i select a new option the alert appers, but i don't know how i send the selected element to function and how i tell to him to show the f.fields_for :unifications.
<div class="form-row">
<label>
<span>Evento</span>
<%= f.select :event, ['Reencaminhar', 'Reclassificar', 'Rejeitar alteracão de diagnóstico', 'Rejeitar reencaminhamento', 'Solicitar esclarecimento', 'Cancelar solicitação', 'Rejeitar cancelamento', 'Responder', 'Unificação', 'Envio de cópia de ID', 'Reiterar', 'Registrar Solicitação', 'Reabrir solicitação', 'Complementar solicitação'], {}, :onChange => "fillForm.test(this)" %>
</label>
</div>
<%= f.fields_for :unifications do |unification_form| %>
<label>
<%= unification_form.link_to_remove "Remover cpf" %>
<%= unification_form.text_field :cpf %>
</label>
<% end %>
requests.coffee
#fillForm =
test: ->
alert('Hello world')

Add a div to hide/show the nested form block:
<div id="attributes-to-display" style="display:none">
<%= f.fields_for :unifications do |unification_form| %>
<label>
<%= unification_form.link_to_remove "Remover cpf" %>
<%= unification_form.text_field :cpf %>
</label>
<% end %>
</div>
Its hidden by default. I don't use coffee script, so a possible solution with plain javascript:
// binds the change event
$("#the_select_id").on('change', function() {
// checks if the selected value is what you expect
if ($(this).val() == "whatever") {
// if so, show the block
$("#attributes-to-display").show();
}
else {
// if not, hide the block
$("#attributes-to-display").hide();
// and clean the cpf input box
$("#the_nested_input_id").val('');
}
});
You need to replace the ids (#) with the correct form ids, as I don't have enough information to provide it.

Related

How do I make javascript change the element of specific element in rails

I have a little bit of a problem with the id names for my rails app where I want javascript to disable/enable a field with the press of a button.
I have a big index page where people can rate a bunch of pictures on the index page itself. For this I am using the #posts.each do |post| method.
The user should then be able to rate the picture with a slider, and after the slider was used, the range should be disabled. If the user wants to change the rating it is possible to click on "CHANGE", which enables the slider again.
Problem I have right now is with the enable function. I have a bunch of posts on the index page, and and all of the sliders and buttons have the same class names and ids. I have tried to give each slider and button a specific id with id:'ratingPost#{post.id}', but the problem then is that I cannot get javascript to know what is the postid that was just clicked.
Can you help me here?
Thank you very much!
My code is here:
#posts_index.html.erb
<% #posts.each do |post| %>
...
<% if post.ratings.find_by(user_id: current_user.id) %>
<%= form_for [post, post.ratings.find_by(user_id: current_user.id)] do |f| %>
<%= f.range_field :score, class:"form-control-range slider", id:"ratingPost", onMouseUp:"submit()", onTouchEnd:"submit()", :disabled => true, data: { source: post} %>
<% end %>
<% else %>
<%= form_for [post, #rating] do |f| %>
<%= f.range_field :score, class:"form-control-range slider", id:"formControlRange", onMouseUp:"submit()", onTouchEnd:"submit()"%>
<% end %>
<% end %>
...
<% if post.ratings.find_by(user_id: current_user.id) %>
<h2><%= post.ratings.where(user_id: current_user.id).last.score %>%</h2>
<button id="RatingChangeButton"><p>CHANGE</p></button>
<% end %>
</div>
</div>
</div>
<% end %>
<script>
document.getElementById("RatingChangeButton").addEventListener("click", enableRating);
function enableRating() {
document.getElementById("ratingPost").disabled=false;
}
</script>
Ideally you should not have multiple elements with the same ID in your HTML, instead use class.
Now to your question, what you can do is store the id of the post in a data attribute for your buttons and rating fields, and access that id in the javascript function to identify the corresponding slider. Something like,
https://codepen.io/anujmiddha/pen/NWRwwLL
<p>Post 1: <input class="ratingPost" data-post-id="1" disabled></p>
<p>Post 2: <input class="ratingPost" data-post-id="2" disabled></p>
<p>
<button class="RatingChangeButton" data-post-id="1" onClick="enableRating(this)">
<p>CHANGE 1</p></button>
</p>
<p>
<button class="RatingChangeButton" data-post-id="2" onClick="enableRating(this)">
<p>CHANGE 2</p></button>
</p>
function enableRating(view) {
let element = document.querySelector('[data-post-id="' + view.dataset.postId + '"],[class="RatingChangeButton"]');
element.disabled = false;
element.value = "enabled";
}

Rails - Synchronise button clicks among tabs

I have a view with bootstrap-tabs. The tabs are generated dynamically.
<%= form_with(:id => 'my-form', model: [:admin, #island], local: true) do |form| %>
<div class="tab-content bg-light" id="tabs-from-locales-content">
<%= available_locales.each_with_index do |locale, i| %>
<div
class="tab-pane fade <%= 'show active' if i == 0 %>"
id="<%= locale.downcase %>"
role="tabpanel"
aria-labelledby="<%= locale.downcase %>-tab">
<%= render partial: 'island_form', locals: {counter: i, locale: locale, f: form} %>
</div>
<% end %>
</div>
...
...
A tab represents each available localization of the app.
The model of the form contains two nested attributes. These attributes have 1 to many relationship with the model. So the user can add multiple of these from the form. Their fields can be generated dynamically:
(For simplicity I include in the question only one. This is a part of _island_form.html.erb partial.)
<div class="form-group ports-div">
<%= f.label :port %> </br>
<%= f.fields_for :ports do |builder| %>
<%= render 'port_fields', f: builder %>
<% end %>
<%= link_to_add_fields t('form.add_port'), f, :ports %>
</div>
<div class="form-group">
<%= f.label :airport %> </br>
<%= f.fields_for :airports do |builder| %>
<%= render 'airport_fields', f: builder %>
<% end %>
<%= link_to_add_fields t('form.add_airport'), f, :airports %>
</div>
And the port_fields partial:
<fieldset>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.hidden_field :_destroy %>
<%= link_to t('form.remove'), '#', class: 'remove_fields' %>
</fieldset>
The link_to_add_fields helper method:
def link_to_add_fields(name, f, association)
# Builds an instance of the association record.
new_object = f.object.send(association).klass.new
# Grabbing ruby's object id.
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + '_fields', f: builder)
end
link_to(name, '#', class: 'add_fields', data: { id: id, fields: fields.gsub('\n', '') })
end
What I want to achieve is to synchronize the addition and removal of these fields among the available tabs. So when the user clicks Add field on the first tab all the other tabs will follow this action. Same for field removal.
My relevant js file for the Add button looks like this. I have tried many combinations between the trigger, triggerHandler and stopPropagation, although most of the times I am getting StackOverflow exception and the fields are added only to the tab that I clicked the add button.
(Since I pass a class (.add_fields) in the selector isn't that supposed to be attached to all the elements with class .add_fields?)
form.on('click', '.add_fields', function (event, param) {
event.stopPropagation();
event.preventDefault();
var time = new Date().getTime();
var regexp = new RegExp($(this).data('id'), 'g');
$(this).before($(this).data('fields').replace(regexp, time));
$('.tab-pane').each(function (index) {
$('.add_fields').trigger("click", ["custom_click"]);
console.log('Tab - ' + index);
})
});
EDIT
I am getting somewhere with that code:
$('.ports-div').each(function (index) {
$(this).find('.add_fields').each(function () {
this.click();
})
});
Although, at all the tabs, instead of 1 field 6 fields are added. I added the ports-div to the div that wraps all the related elements.
Complete rewrite...
The problem as you have seen is to be that your click event handler is triggering a click on all tab-panes, including the one you are handling currently. You are also not just clicking the a single item in the loop, but all that match '.add_fields'. This then leads to the click being handled again, recursively.
To prevent this, I suggest calling a function to add your fields directly, rather than triggering a click. If triggering a click is just easiest in your situation, consider the following example that does roughly what you want without the recursive error.
https://jsfiddle.net/3ftf0j8e/1/
Dummy HTML
<a class="add_fields" id='1'>link 1</a>
<a class="add_fields" id='2'>link 2</a>
<a class="add_fields" id='3'>link 3</a>
<a class="add_fields" id='4'>link 4</a>
Sample Javascript
$(document).on('click', '.add_fields', function (event, param) {
event.preventDefault();
var clicked_id = $(this).attr('id');
console.log('clicked id:' + clicked_id);
$(this).addClass('done');
$(this).addClass('clicked');
// Just click items that have not been clicked
var els = $('.add_fields').not('.clicked');
console.log(els);
els.trigger("click");
setTimeout(function(){
if($('.add_fields').length == $('.add_fields.done').length)
$('.add_fields').removeClass('clicked');
})
});
CSS
a.clicked {
background-color: yellow;
}
a.done {
color: red;
}
As you can see now, each fires just once. The setTimeout at the end allows the DOM to update before clearing the clicked classes.

Why does jQuery only enter the function in some links?

I am relatively new to jQuery and JavaScript, so I'm having this issue, hope you can help me through it.
I am making an e-commerce page for selling cloths in Rails 4, so I am focusing more than I used to in the form of displaying forms and all of that, so it can be more attractive to the users. Because of that, I have the forms hidden and I set the values to it via jQuery.
The problem can be divided in two parts:
The first part of the problem is in the cloths#show where the user add the cloth to his cart:
Form:
<%= form_for #order_item, remote: true do |f| %>
<h5>Select a size</h5>
<div class="input-sizes">
<% #sis.each do |si| %>
<%= link_to si.size.letter, "#{si.size_id}", class:"normal-size" %>
<% end %>
</div>
<%= f.hidden_field :size_id, id: "size-input" %>
<h5>Select a color</h5>
<div class="input-colors">
<% #cos.each do |co| %>
<%= link_to "#{co.color_id}", id: "link-circle" do %>
<div id="circle" style="background: <%= co.color.hex %>">
<div id="mini-circle"></div>
</div>
<% end %>
<% end %>
</div>
<%= f.hidden_field :color_id, id: "color-input" %>
...
<% end %>
jQuery:
//Select size
$('a.normal-size').on('click', function(e){
e.preventDefault();
var value = $(this).attr("href");
$('#size-input').val(value);
$(this).removeClass("normal-size").addClass("selected").siblings().removeClass("selected").addClass("normal-size");
});
//Select Color
$('#link-circle').on('click',function(e){
e.preventDefault();
var value = $(this).attr("href");
$('#color-input').val(value);
$(this).children().children().css('opacity', '1');
return false;
});
The set of the size input works perfectly but in the color input the user only can select the first color because if the user wants to select the second, the jQuery function doesn't gets called so it goes to the link (I tried even writing e.preventDefault() and return false at the same time).
The second part of the problem comes in the cart#show when the user can see the summary of all his cloths. In this it is displayed a table with each row being a cloth, here the user can see the image of the cloth, description, selected color, selected size and price. The color and size section is made for the user so he can edit his cloth if he changes his mind and select another size or color. The issue in here is in both size and color and I don't know why. In the color section happens exactly the same as above, but in the size section he can only make one click because if he makes another one the jQuery function doesn't get called. Is like if the click only works once:
Form:
<%= form_for (order_item), remote: true,:url => "/order_items/#{order_item.id}", :html=>{:id=>'item_form_cart'} do |f| %>
<div class="input-colors col-md-1">
<% #cos.each do |co| %>
<% if co.cloth == order_item.cloth %>
<% if co.color_id == order_item.color_id %>
<%= link_to "#{co.color_id}", id: "link-circle" do %>
<div id="circle" style="background: <%= co.color.hex %>;">
<div id="mini-circle" style="opacity: 1;"></div>
</div>
<% end %>
<% else %>
<%= link_to "#{co.color_id}", id: "link-circle" do %>
<div id="circle" style="background: <%= co.color.hex %>;">
<div id="mini-circle"></div>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
</div>
<%= f.hidden_field :color_id, id: "color-input" %>
<div class="input-sizes col-md-2">
<% #sis.each do |si| %>
<% if si.cloth == order_item.cloth %>
<% if order_item.size_id == si.size_id %>
<%= link_to si.size.letter, "#{si.size_id}", class:"selected" %>
<% else %>
<%= link_to si.size.letter, "#{si.size_id}", class:"normal-size cart-el" %>
<% end %>
<% end %>
<% end %>
</div>
<%= f.hidden_field :size_id, id: "size-input-cart" %>
...
<% end %>
jQuery:
//Select size
$('a.normal-size.cart-el').on('click',function(e){
e.preventDefault();
var value = $(this).attr("href");
$(this).parent().next('#size-input-cart').val(value);
$(this).removeClass("normal-size").addClass("selected").siblings().removeClass("selected").addClass("normal-size");
$(this).closest('#item_form_cart').submit();
});
In here I only make the size function because I didn't know how to fix the color one, but this also has a bug, that only gets called the first time. This happens no matter which cloth's size you select, e.i. if I change the first cloth's size works good but if I want to change again the same cloth's size or change the size of another cloth the jQuery function doesn't get called.
Hope you can help me,
Thanks in advance.
Edit:
I have investigated more about why is happening the second problem and I saw that it can be related to Ajax, because in my form if a user changes the size or color it gets done by Ajax, as we can see here. I tried the second solution but it did not work:
$('.well').on('click','a.normal-size.cart-el',function(e){
e.preventDefault();
var value = $(this).attr("href");
$(this).parent().next('#size-input-cart').val(value);
$(this).removeClass("normal-size").addClass("selected").siblings().removeClass("selected").addClass("normal-size");
$(this).closest('#item_form_cart').submit();
});
$('.well').on('click','.link-circle.cart-el',function(e){
e.preventDefault();
var value = $(this).attr("href");
$(this).parent().next('#color-input-cart').val(value);
$(this).siblings().children().children().css('opacity', '0');
$(this).children().children().css('opacity', '1');
$(this).closest('#item_form_cart').submit();
});
And here is render each item in the view:
<div class="order_items">
<% #order_items.each do |order_item| %>
<div class = "well">
<%= render 'carts/cart_row', cloth: order_item.cloth, order_item: order_item, show_total: true %>
</div>
<% end %>
</div>
Your select color function is attached to the id "link-circle". It looks like you're potentially rendering multiple elements with the same id. Your select size function is based on a class which you can have on multiple elements. You cannot however have the same id on multiple elements according to w3 . Try changing "link-circle" to a class like so..
<%= link_to "#{co.color_id}", class: "link-circle" do %>
and the your selector to..
$('.link-circle').on('click',function(e){

Rails 3.2 - form erroneously submitted multiple times

I have the model Box, and each Box has many box_videos (another model). I want the user to be able to add box_videos to the box, so I created the following edit form for that (after creation of #box):
<%= form_tag "/box_videos", { method: :post, id: "new_box_videos", remote: true } do %>
<%= text_field_tag "box_videos[][link]", '' %>
<%= text_area_tag "box_videos[][description]", '' %>
<%= hidden_field_tag("box_videos[][box_id]", #box.id) %>
<%= hidden_field_tag("box_videos[][user_id]", current_user.id) %>
<div class="another_video">Add Another Video</div>
<%= submit_tag "Save Videos" %>
<% end %>
<%= form_for(#box) do |f| %>
<%= f.text_field :name %>
<%= f.text_field :size %>
<%= f.text_field :all_other_attributes %>
<%= f.submit "Create Box" %>
<% end %>
And some Javascript to facilitate adding more box_videos in one click.
<script>
$('.another_video').click(function() {
$('#new_box_videos').prepend('<input id="box_videos_link" name="box_videos[][link]" placeholder="Link to a youtube video." style="width: 18em;" type="text" value=""><textarea id="box_videos_description" name="box_videos[][description]" placeholder="Describe this video." style="width: 18em;"></textarea><br/><br/><input id="box_videos_box_id" name="box_videos[][box_id]" type="hidden" value="' + gon.box_id.toString() + '"><input id="box_videos_user_id" name="box_videos[][user_id]" type="hidden" value="' + gon.user_id.toString() + '">');
});
</script>
The above code works, in that params[:box_videos] when submitting three box_videos is as follows:
[{"link"=>"https://www.youtube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU
", "description"=>"foo", "box_id"=>"63", "user_id"=>"16"}, {"link"=>"https
://www.youtube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU", "description"
=>"bar", "box_id"=>"63", "user_id"=>"16"}, {"link"=>"https://www.you
tube.com/watch?feature=player_detailpage&v=dpAP8bq3ddU", "description"=>"hello world",
"box_id"=>"63", "user_id"=>"16"}]
In my controller I would just create a box_video object for every hash in the array and it works out just fine. BUT the problem comes when each time I submit the nested form_tag form, I send multiple requests to the controller action! Which means there are duplicates being created.
I can think of adding logic to the box_videos controller create action to check for duplicate content, but it seems rather hacky. Can anyone please let me know why this is happening?
You can't have nested form elements according to the html spec (see this answer).
You might want to use nested forms for this, it provides the creation of associated models via jquery with useful form_helper wrappers.

Hide/Show content when checkbox is clicked?

Hey, I have a code segment here:
<div class="label"><%= f.label :course %> <span>(Does this relate to a specific course?)</span></div>
<%= check_box_tag(:no_course) %>
<%= label_tag(:no_course, "None") %><br />
<%= f.collection_select(:course_id, #current_account.courses.find(:all, :order => "name"), :id, :name) %>
How could I make it so that
<%= f.collection_select(:course_id, #current_account.courses.find(:all, :order => "name"), :id, :name) %>
only shows when
<%= check_box_tag(:no_course) %>
Is selected using javascript?
Thanks!
If you are doing it with jQuery, do this in your document.ready:
if(!$('.no_course:checked').length) {
$('myElementSelectedByClass').hide();
}
I am assuming here that it is originally shown and you are hiding it when the page loads. The basic premise is to get your checkbox DOM Element (in this case I get by class name) and see if the checked attribute is set. If not then hide it.

Categories

Resources