How to show/hide fields on modal in Ruby on Rails? - javascript

I am writing an application using Rails 4.1 and Ruby 2.1.2. It has the following show.html.erb view for an Assignment Model:
<h1><%= "#{'Source URL'}"%> <div class="source-url"><%= #assignment.source.url %></div></h1>
<%= link_to new_assignment_contact_path(#assignment), id: 'display_modal', class: "add-btn btn-big" do %>
<i class="i15"></i> Add Contact
<% end %>
<br />
<br />
<table>
<thead>
<tr>
<th>Contact Email Address</th>
<th>Contact Name</th>
<th>Title</th>
<th>Phone</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<% #contacts.each do |contact| %>
<tr>
<td><%= contact.contact_email %></td>
<td><%= contact.contact_name %></td>
<td><%= contact.contact_title %></td>
<td><%= contact.contact_phone_number %></td>
<td><%= contact.notes %></td>
</tr>
<% end %>
</tbody>
</table>
<br />
<%= link_to "Completed", change_status_assignment_path(#assignment, status: "Completed"), method: :patch, class: "action-btn" %>
When a user clicks the New Contact button, a modal is displayed using the following form:
<%= form_for(#contact, url: assignment_contacts_path(#assignment), remote: true) do |f| %>
<div class="cols">
<div class="col">
<div class="field">
<%= f.email_field :contact_email, autofocus: true, placeholder: "Contact email" %>
</div>
<div class="field">
<%= f.text_field :contact_name, placeholder: "Contact Name" %>
</div>
<div class="field">
<%= f.text_field :contact_title, placeholder: "Contact Title", class: "ico-doc" %>
</div>
<div class="field">
<%= f.text_field :contact_phone_number, placeholder: "Contact Phone Number", class: "ico-phone" %>
</div>
</div>
<div class="col">
<div class="field">
<%= f.text_field :contact_domain, placeholder: "Contact Domain", class: "ico-www" %>
</div>
<div class="field">
<%= f.select :notes, ["404 Not Found", "Page Not Loading", "Site Error", "Web Page Not Available", "Log In Required", "Privacy Error", "Blank Page", "Redirect Url Change >>", "Contact Form Only >>", "Irrelevant >>"], { prompt: "Notes" }, { class: "ico-globe" } %>
</div>
<div class = "field">
<%= f.text_field :redirect_url_change, placeholder: "Enter Redirect URL" %>
</div>
<div class = "field">
<%= f.text_field :contact_form_only, placeholder: "Enter Contact Form URL" %>
</div>
<div class = "field">
<%= f.select :irrelevant, ["Foreign Site", "Lead Gen Site", "Job Listing", "Domain For Sale", "Forum", "FAQ", "Other >>"], { prompt: "Select Reason Irrelevant", style: 'display:none'} %>
</div>
<div class = "field">
<%= f.text_field :other, placeholder: "Enter other reason" %>
</div>
</div>
</div>
<div class="actions">
<%= f.submit "Submit", class: "action-btn" %>
</div>
<% end %>
The :redirect_url_change, :contact_form_only, :irrelevant, and :other fields on the modal are hidden by css using display:none. But, based on the option chosen in the :notes select element, the app needs to display one of these fields when a corresponding option with that value is selected.
The only way to do this in Rails that I know of is with jQuery or Coffeescript.
But, I'm not sure how to write the jQuery/Coffeescript to do this. And just as importantly, since this form is on a modal, where should this jQuery or Coffeescript be written? I've tried to just add a simple alert in the app/assets/javascript/contacts.js.coffee, but that doesn't get triggered to display. Am I putting this in the wrong place?
Please share some code you think would work for this and tell me where it should be placed.
EDIT: Thanks to Lanny I got the following jQuery working in the javascript console in the browser:
$("select[name='contact[notes]']").change(function () {
if ($(this).val() == "Redirect Url Change >>") {
$("input[name='contact[redirect_url_change]']").show();
$("input[name='contact[contact_form_only]']").hide();
$("select[name='contact[irrelevant]']").hide();
$("input[name='contact[other]']").hide();
}
else if ($(this).val() == "Contact Form Only >>") {
$("input[name='contact[redirect_url_change]']").hide();
$("input[name='contact[contact_form_only]']").show();
$("select[name='contact[irrelevant]']").hide();
$("input[name='contact[other]']").hide();
}
else if ($(this).val() == "Irrelevant >>") {
$("input[name='contact[redirect_url_change]']").hide();
$("input[name='contact[contact_form_only]']").hide();
$("select[name='contact[irrelevant]']").show();
$("input[name='contact[other]']").hide();
}
else {
$("input[name='contact[redirect_url_change]']").hide();
$("input[name='contact[contact_form_only]']").hide();
$("select[name='contact[irrelevant]']").hide();
$("input[name='contact[other]']").hide();
}
})
$("select[name='contact[irrelevant]']").change(function () {
if ($(this).val() == "Other >>") {
$("textarea[name='contact[other]']").show();
}
else {
$("textarea[name='contact[other]']").hide();
}
})
When I open up the dialog modal, paste the above into the console and then press Run it works perfectly. But, it still doesn't run in the application. I've tried to put it in the application.js and also in the contact.js, but neither one works. At this point, since the code works in the console, I suspect it might be either something needs to be run to load the code into the browser, or else something is interfering. I am running Turbolinks right now, but I don't think it is necessary for the application.
Can someone please help me get the rest of the way by showing where this needs to be put and how to call it, or assisting me in troubleshooting what is interfering?
Thanks

Let's walk through this...
I'm going to implement this rule: If a user selects, say '404' from the Notes select, make the 'redirect_url_change' input appear.
I know your exact logic might be different. Hopefully mixing-and-matching jQuery selectors can get you the rest of the way.
Using jQuery...
application.js
$(document).on("page:load", function() {
$("select[name='contact[notes]']").change(function () {
alert("Howdy!");
if ($(this).val() == "404") {
$("input[name='contact[redirect_url_change]']").css("display", "block");
}
});
});
One line up there is for debugging. Sometimes JS can fail in somewhat silent ways and it's hard to determine why events aren't firing. I use alert() to help highlight which things are happening, and which aren't. YMMV.
This script doesn't explicitly have to go in application.js, but it should go into a .js file in your javascripts folder so Rails compiles it into your app's JavaScript.
Some references:
jQuery Attribute Equals Selector
jQuery .change() method
Javascript if ()
jQuery .val() method
jQuery .css() method

Related

Is jQuery .append breaking my Rails form and causing it not to fully render?

Running into a strange issue. I using jQuery's .append() to place a <div> containing a Rails form next to the paragraph tag a user selects in my Rails app.
First off, before adding the jQuery effect, I tested this by placing a the form on the page. It loaded, worked, and posted the user's text to my database correctly.
Second, I added this effect, and the <div> with the form does load, but weirdly, you cannot click into it. The text cursor will appear for a moment, disappear, and the user can not actually enter anything in the text_field.
Here's what I am running:
view.html.erb
<% #posts.each do |post| %>
<h2 id="title"><%= post.title %></h2>
<div id="paragraph"><%= markdown(post.content) %></div>
<% end %>
<div class="exampleToggle"> <!-- div that should append -->
<%= form_for :comments do |f| %>
<div class="form-group">
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="field">
<%= f.label :post %><br>
<%= f.text_area :post, class: "form-control" %>
</div>
</div>
<div class="actions">
<%= f.submit "Save", class: "btn btn-success-outline" %>
</div>
<% end %>
</div>
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$("p").click(function(){
$(this).append(whatever);
});
});
</script>
As I mentioned, the form does load and looks how it should after clicking on a paragraph element; however, the form will not accept text input.
Also, for clarity's sake, here is the CSS involved with the placing the <div> to the right of the clicked paragraph:
{
position: relative;
}
.exampleToggle {
position: absolute;
top: 0;
margin-left: 8%;
left: 100%;
width: 35%;
height: 100px;
}
Any idea why this is breaking the form/form helper?
EDIT 1: For clarity, the central issue here seems to be the Focus effect. To try and fix this, I updated the Javascript to allow for value entry when the .field class is activated, but it did not fix it. Here's what I tried.
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$("p").click(function(){
$(this).append(whatever);
});
$('.field').click(function () {
var val = $(this).val();
if (val == "") {
this.select();
}
});
});
</script>
You have given $("p"), but there is no p tag in your question.
Also you are looping the same id to different fields which is not correct, ID should be unique. Instead take the class instead of id.
You should take a class to the paragraphed_id field and should write the jquery functions based on the required classes.
Try this,
<% #posts.each do |post| %>
<h2 id="title"><%= post.title %></h2>
<div class="paragraph"><%= markdown(post.content) %></div>
<% end %>
<div class="exampleToggle"> <!-- div that should append -->
<%= form_for :comments do |f| %>
<div class="form-group">
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="field">
<%= f.label :post %><br>
<%= f.text_area :post, class: "form-control" %>
</div>
</div>
<div class="actions">
<%= f.submit "Save", class: "btn btn-success-outline" %>
</div>
<% end %>
</div>
<script>
$(document).ready(function(){
var whatever = document.getElementsByClassName("exampleToggle");
$(".paragraph").click(function(){
$(this).append(whatever);
});
});
</script>

Rails: How to keep at least one form section displayed in edit forms

Following railscasts #196 Nested Model Form, I was able to implement a working add/remove function to my forms.
The problem is when I remove the last form section, submit, and edit form. The form section is empty and displays the "+ Add More" link only (since I removed the others when I submitted the form?).
By default, I used #userprofile.programs.build so the form builds one program section when it is first created.
userprofiles_controller.rb
def new
#userprofile = Userprofile.new(params[:userprofile])
#userprofile.programs.build
end
_form.html.erb
<h3>Programs</h3>
<%= f.fields_for :programs do |builder| %>
<%= render 'program_fields', f: builder %>
<% end %>
<div class="field">
<%= link_to_add_fields "+ Add More", f, :programs %>
</div>
_program_fields.html.erb
<fieldset>
<%= f.select :program_name, [['A'], ['B']], { label: "Program Name: ", :include_blank => "Please select a program...", { class: "form-control" } %>
<%= f.select :program_role, [['Student'], ['Teacher']], { label: "Program Role: ", :include_blank => "Please select your role..." }, { class: "form-control" } %>
<%= f.hidden_field :_destroy %>
<%= link_to "Delete", '#', class: "remove_fields" %>
</fieldset>
Not sure how or if I need to add a condition and build in the userprofiles_controller.rb when the edit form is empty? So that if the whole section is removed, it still shows one form section by default and not just a empty section with the link to "+ Add More".
Or if there is some way so the first program section cannot be deleted. But the rest that are added has the "Delete" function?
Any insight would help, thank you!
UPDATE:
userprofiles.js.coffee
$(document).on 'click', 'form .remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
Let's say you have the following HTML -- I added a couple of classes:
_form.html.erb
<div class="programs-form">
<h3>Programs</h3>
<div class="programs-fields">
<%= f.fields_for :programs do |builder| %>
<%= render 'program_fields', f: builder %>
<% end %>
</div>
<div class="field">
<%= link_to_add_fields "+ Add More", f, :programs %>
</div>
</div>
_program_fields.html.erb
<fieldset class="program">
<%= f.select :program_name, [['A'], ['B']], { label: "Program Name: ", :include_blank => "Please select a program...", { class: "form-control" } %>
<%= f.select :program_role, [['Student'], ['Teacher']], { label: "Program Role: ", :include_blank => "Please select your role..." }, { class: "form-control" } %>
<%= f.hidden_field :_destroy %>
<%= link_to "Delete", '#', class: "remove_fields" %>
</fieldset>
Then, assuming you have jQuery loaded somewhere in your app, you can run the following script:
$(function () {
$('a.remove_fields').hide();
$('.programs-fields').on('change', function (ev) {
if ($('fieldset.program').length > 1) {
$('a.remove_fields').show();
}
});
});
I haven't had the chance to set up a test environment and run this code, so please let me know if you have any problems -- I'll be more than happy to fix any bugs!

Javascript events firing multiple times in rails app?

I am trying to set up a form so that it submits via ajax when I hit enter. To do this I wanted to trigger the form to submit when the enter key is pressed on the input field. However the keyup event for the enter key keeps firing multiple times if the key is held down any longer than a split second which in turn sends lots of ajax requests causing the browser to crash.
I cannot figure out why the event keeps firing multiple times. Here is the view for the page:
<div class="page-content">
<div class="l-edit-header">
<h1>Edit</h1>
<div class="piece-header">
<%= image_tag #piece.user.avatar_url(:small), class: "avatar-small" %>
<div class="piece-header-info">
<h1><%= #piece.title %></h1>
<em>
By <%= link_to #piece.user.username, user_path(#piece.user) %>
<%= #piece.created_at.strftime("%B %d, %Y") %>
</em>
</div>
</div>
</div>
<div class="l-edit-main">
<div class="piece-main">
<%= image_tag #piece.image_url %>
<p id="piece-description" class="piece-main-description"><%= #piece.description %></p>
<div class="piece-main-links">
<%= link_to "Delete", piece_path(#piece), method: :delete if current_user == #piece.user %>
</div>
</div>
</div>
<div class="l-edit-side">
<div class="form-container">
<%= form_tag piece_tags_path(#piece), id: "new_tag", remote: true do %>
<%= label_tag :new_tag, "New Tag"%>
<%= text_field_tag :tag, "", data: {autocomplete_source: tags_url}, placeholder: "Add a tag and press Enter" %>
<div id="tags" class="piece-tags">
<%= render partial: "tags/delete_tag_list", locals: {piece: #piece, method: :delete} %>
</div>
<% end %>
</div>
<div class="form-container">
<%= simple_form_for #piece do |f| %>
<%= f.association :category, include_blank: false %>
<%= f.input :published, as: :hidden, input_html: {value: true} %>
<%= f.input :title %>
<%= f.input :description %>
<div class="form-submit">
<%= f.button :submit, "Publish" %>
</div>
<% end %>
</div>
</div>
</div>
and here is the Javascript for the tag form I am trying to work with:
var tagReplace = {
init: function(){
//Replace "#tags" with new updated tags html on tag create
$("#new_tag").on("ajax:success", function(e, data, status, xhr){
$("#tags").html(data);
tagReplace.init();
$("#tag").val("");
});
//Replace "#tags" with new updated tags html on teg delete
$("#tags a[data-remote]").on("ajax:success", function(e, data, status, xhr){
$("#tags").html(data);
tagReplace.init();
});
$("#new_tag").on("keydown", function(e){
if (e.which == 13){
event.preventDefault();
}
});
$("#tag").on("keyup", function(e){
if (e.which == 13){
$("#new_tag").submit();
console.log("pressed enter on new tag");
}
});
},
getTags: function(){
$.get( $("#tag").data("autocomplete-source"), tagReplace.initAutocomplete);
},
initAutocomplete: function(tagsArray){
$("#tag").autocomplete({
source: tagsArray
});
}
};
//Initalize
$(document).on('ready page:load', function () {
tagReplace.init();
});
As you can see I have prevented the default behaviour for the return key being pressed on the form and have added a console.log to count the number of times the event is being triggered.
I thought this could be to do with the fact I am using turbolinks but I can't seem to figure out why.
How can I ensure that the event only gets triggered one time for each time the enter key is pressed? At the moment the Javascript is crashing the browser when I hit enter.
You are calling tagReplace.init(); 2 times within itself and, as #Dezl explains in his answer related to this topic, "If you have delegated events bound to the document, make sure you attach them outside of the ready function, otherwise they will get rebound on every page:load event (causing the same functions to be run multiple times)."

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.

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();
}

Categories

Resources