Rails 3.2 - form erroneously submitted multiple times - javascript

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.

Related

How to check policies for objects created through AJAX

In my program one Board can have many Sections. On Boards#show I list all Sections for this Board. A user can create, edit and delete Sections for Boards this user has created.
I use Pundit for my authorizations.
My problem is that after having added AJAX to the create Section action, my policy checks don't work anymore. The AJAX call is working fine in that Sections are added but the "Edit" and "Delete" links are only displayed after I reload the page.
Boards#show
<% if policy(#board).edit? %>
<p><strong>Create a new Section</strong></p>
<%= render 'sections/shared/form', board: #board, section: #section %>
<% end %>
<br>
<p><strong>Sections</strong></p>
<div id="sections">
<% #board.sections.order(id: :asc).each_with_index do |section, index| %>
<span><%= "#{index + 1}. #{section.title}" %></span>
<% if policy(section).edit? %>
<%= link_to "Edit", edit_board_section_path(#board, #board.sections[index]) %>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(#board, #board.sections[index]), method: :delete) %>
<% end %>
<br>
<% end %>
</div>
Form partial
<%= simple_form_for([#board, #section], remote: true, authenticity_token: true) do |f| %>
<%= f.input :title, id: "section_title" %>
<%= f.submit "Save" %>
<% end %>
AJAX call
var newSection = "<span><%= "#{#board.sections.length}. #{#section.title}" %></span><br>";
var sections = document.getElementById('sections');
sections.insertAdjacentHTML('beforeend', newSection);
var sectionTitle = document.getElementById('section_title');
sectionTitle.value = '';
I think the problem is that the newly created Section isn't inserted into the loop and thus the policy cannot be checked. How could I refactor my code to solve this?
You should be able to render you HTML templates into your js.erb file. See the post from the DHH https://signalvnoise.com/posts/3697-server-generated-javascript-responses how to use this properly. It is an older post so maybe some things changed but you should know what to search for. But if it still works this way, you should be able to do something like this:
Extract section to a partial _section.html.erb
<span><%= "#{index + 1}. #{section.title}" %></span>
<% if policy(section).edit? %>
<%= link_to "Edit", edit_board_section_path(#board, #board.sections[index]) %>
<% end %>
<% if policy(section).destroy? %>
<%= link_to("Delete", board_section_path(#board, #board.sections[index]), method: :delete) %>
<% end %>
<br>
Use this partial also in your Boards#show file.
Render your partial in the js.erb file
var newSection = "<span><%=j render #section %></span><br>";
Something like this should work, but I don't use server generated javascripts so they could have changed some things. But hopefully it will help you at least with the direction.

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){

resetting form via jQuery not clearing ckeditor cktext_area field

I have following form
<div id="post-close-updates-form">
<%= form_for [#investment,#post_close_update], remote: true do |f| %>
<div class="form-group">
<%= f.label :content %>
<%= f.cktext_area :content %>
</div>
<%= f.submit "Update", class: "btn btn-primary" %>
<% end %>
</div>
and my jquery code is
$("#post-close-updates-form form")[0].reset();
but it is not clearing cktext_area content...while if i put normal html textarea then it works fine.
so how do i clear ckeditor cktext_area via js/jquery
ok here is the answer given by developer of ckeditor #galetahub
$("#post-close-updates-form form")[0].reset();
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].updateElement();
}
but above one not worked for me so made some chages that works
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].setData(" ");
}
setData() is to set the data is cktext_area
now if u want to get the data from cktext_area in js then use this
for (instance in CKEDITOR.instances){
CKEDITOR.instances[instance].getData();
}

Clear a text_field after create w/o refresh

Right now I'm using AJAX for creating comments. However when I hit enter the textfield remains populated. I want the field to refresh (and still be able to write in another comment). no errors but it still doesn't refresh the textfield it seems. The create part works fine.
create.js/erb: (need to fix the second line here so that it fully replaces)
$('#<%= dom_id(#micropost) %>').html("<%= escape_javascript(render(:partial => #micropost.comments))%>")
$("#comment_field_<%=#micropost.id%>").replaceWith("<%= escape_javascript(render 'shared/comment_form', micropost: #micropost) %>")
Microposts/_micropost:
<span id="<%= dom_id(micropost) %>"><%= render micropost.comments %></span>
<span id="comment_field_<%=micropost.id%>"><%= render 'shared/comment_form', micropost: micropost if signed_in? %></span>
Shared/Comment_form:
<%= form_for #comment, id:"comment_form", remote: true do |f| %>
<%= hidden_field_tag :micropost_id, micropost.id %>
<div id="comment_field">
<%= link_to gravatar_for((current_user), size: 29) %>
<%= f.text_field :content, placeholder: "Say Something...", id: "comment_text_field", :style => "width: 508px; text-indent: 5px" %>
</div>
<% end %>
Needed to only use jquery to clear the text_field by setting empty value
create.js.erb
$("#comment_text_field_<%=#micropost.id%>").val("")
best practice to use a unique dynamic ID but probably would have worked either way.
Shared/Comment_form:
<%= f.text_field :content, id: "comment_text_field_#{micropost.id}" %>

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