Rails assign ids to elements in builder to access using jQuery - javascript

I'm using the Rails builder to build multiple :pay objects
<%= f.fields_for :pay do |builder| %>
<%= render "non_taxable_pays", :f => builder %>
<% end %>
This is rendering _taxable_pays multiple times as desired
_taxable_pays.html.erb
<div class="row">
<%= f.hidden_field :employee_id %>
<%= f.hidden_field :ee_pay_id %>
<%= f.hidden_field :pay_sub_head_id %>
<div class="col-md-5">
<div class="col-md-5">
<div class="form_indent1"><div class="form_indent1"><%= f.object.ee_pay.company_pay.description %></div></div>
<div class="form_spacer"></div>
</div>
<div class="col-md-7">
<div class="form_indent1"><span>€ </span><%= f.text_field :rate, value: number_to_currency(f.object.rate), class: "currency_input" %></div>
<div class="form_spacer"></div>
</div>
</div>
<div class="col-md-7">
<div class="col-md-4">
<div class="form_indent1"><%= f.text_field :amount, value: number_to_currency(f.object.amount, :unit => ""), class: "number_input" %></div>
<div class="form_spacer"></div><br />
</div>
<div class="col-md-4">
<div class="form_indent1"><%= f.object.ee_pay.company_pay.units %></div>
<div class="form_spacer"></div>
</div>
<div class="col-md-4">
<div class="form_indent1"><span>€ </span><%= number_to_currency(f.object.rate*f.object.amount) %></div>
<div class="form_spacer"></div>
</div>
</div>
</div>
My issue is in the partial _taxable_pays above. In the last section I have code that displays the value * the rate f.object.rate*f.object.amount. This is great when the page is first displayed as it calculates it based on the values in the database.
I'm now looking to dynamically update this field using jQuery if the user changes the values in the text_fields holding the :rate or :amount. But for the life of me I can't figure out how I should assign id's to the different renders or access them using jQuery.
Can anyone point me in the right direction?
Thanks for looking

So this post helped me come up with my solution
Creating unique id for <fieldset> when using form_for Rails Nested Model Form
I put this at the top of the _taxable_pays partial
<% partial_type = "#{f.object.class}" %>
<% partial_id = "#{f.object.id}" %>
and I was then able to assign ids to the desired elements such as
<span id="<%= "#{partial_type}-value-#{partial_id}" %>
Might help someone in the future

Related

Summernote editor not working with nested forms

I'm trying to use cocoon-gem and summernote-gem in my ruby-on-rails app.
This is my model:
class Dog < ApplicationRecord
has_many :pictures, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :pictures, reject_if: :all_blank, allow_destroy: true
end
At first look new or edit page seems alright. First summary field render with summernote correctly. And if I already have several pictures saved to my dog model, they all look perfectly fine on editing page.
But when I click on "add picture" it creates new picture field with summary field not rendered properly with summernote. In html page it looks like this:
<div class="field">
<label class="string optional" for="dog_pictures_attributes_1544609860934_summary">Summary</label>
<textarea data-provider="summernote" name="dog[pictures_attributes][1544609860934][summary]" id="dog_pictures_attributes_1544609860934_summary"></textarea>
</div>
Instead of this:
<div class="field">
<label class="string optional" for="dog_pictures_attributes_1_summary">Summary</label>
<textarea data-provider="summernote" name="dog[pictures_attributes][1][summary]" id="dog_pictures_attributes_1_summary" style="display: none;"></textarea>
<div class="note-editor note-frame">...</div>
</div>
This is my _form.erb: (if relevant)
<%= simple_form_for #dog, defaults: { required: false } do |f| %>
<div class="field">
<%= f.label 'Note' %>
<%= f.text_area :note, 'data-provider': :summernote %>
</div>
<h2> Add pictures </h2>
<%= f.fields_for :pictures do |picture| %>
<%= render 'picture_fields', :f => picture %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add picture', f, :pictures %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And this is my _picture_fields.erb:
<div class='nested-fields'>
<div class="field">
<%= f.file_field :image %>
</div>
<div class="field">
<%= f.label :author %>
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :summary %>
<%= f.text_area :summary, 'data-provider': :summernote %>
</div>
<%= link_to_remove_association "remove picture", f %>
</div>
I also tried to build nested form from scratch with drifting ruby. But it cause the same problem
When dynamically inserting items into the page, you will have to initialize the summernote editor on those items as well. Cocoon has callbacks to allow you to do this.
When loading the page, to initialize summernote, you would do something like
$('[data-provider="summernote"]').each ->
$(this).summernote
(copied from the summernote-rails gem documentation)
But this only works for the "summernote"'s already on the page, it cannot initialize notes that are not yet there.
So, after inserting a nested-items we have to use the same code to initialise the inserted notes. Something like this should work:
$('form').on('cocoon:after-insert', function(e, insertedItem) {
insertedItem.find('[data-provider="summernote"]').each(function() {
$(this).summernote();
})
});
This will look for summernote items in the freshly nested-item and initialise those.
[EDIT: improve javascript to extract it from view]
Javascript files are generally grouped in app/assets/javascripts, if you are editing Dogs let's add a new file dogs.js and enter the following code:
$(document).on('cocoon:after-insert', 'form', function(e, insertedItem) {
insertedItem.find('[data-provider="summernote"]').each(function() {
$(this).summernote();
})
});
Then add this file to your application.js using require dogs.
This code registers an event handler on the document, it will listen to any cocoon:after-insert event triggered on a form. If you have multiple forms using cocoon, you might need a better/more precise css selector (e.g. add a class to the form and add that as well).

Using Javascript/JQuery to Set Rails `hidden_field_tag` Value

I'm trying to use javascript or jquery to make my app recognize whichever div has a class of active as the value for a hidden_field_tag in my erb form_for. So far I have the following structure set up and working:
<div id="q1" class="row gen-quest-div">
<h1>How long do you have?</h1>
<div class="row answers">
<div id="q1-15" class="answer q1-answer col-xs-6 col-sm-3">
<%= image_tag 'min-15.jpg', alt: "Put 15 minutes on the clock!" %>
<p>15 Minutes</p>
</div>
<div id="q1-30" class="answer q1-answer col-xs-6 col-sm-3">
<%= image_tag 'min-30.jpg', alt: "Put 30 minutes on the clock!" %>
<p>30 Minutes</p>
</div>
<div id="q1-45" class="answer q1-answer col-xs-6 col-sm-3">
<%= image_tag 'min-45.jpg', alt: "Put 45 minutes on the clock!" %>
<p>45 Minutes</p>
</div>
<div id="q1-60" class="answer q1-answer col-xs-6 col-sm-3">
<%= image_tag 'min-60.jpg', alt: "Put 60 minutes on the clock!" %>
<p>60 Minutes</p>
</div>
</div> <!-- answers row -->
</div> <!-- question div -->
<div class="btn-primary" id="pre-submit">Pre Submit</div>
<script>
// Selection handling
$( document ).ready(function() {
$('.q1-answer').on('click', function() {
$('.q1-answer').removeClass('active');
$(this).toggleClass('active');
});
$('.q2-answer').on('click', function() {
$('.q2-answer').removeClass('active');
$(this).toggleClass('active');
});
$('.q3-answer').on('click', function() {
$('.q3-answer').removeClass('active');
$(this).toggleClass('active');
});
});
// Form handling
$('#pre-submit').on('click', function() {
if ($('#q1-15').hasClass('active')) {
console.log("So far this works."); // <<<<THIS LINE
}
});
</script>
This successfully selects a div by marking it .active when it is clicked and prints the console.log statement when the answer is selected (just for testing purposes...). Now the hard part is what to replace the marked line with to make the form recognize the answer. I have my form structured like this:
<%= form_for #generator do |f| %>
<div id="q1-form-field">
<%= hidden_field_tag :time, id: "q1-hf" %>
</div>
...
<div class="text-center">
<h3><%= f.submit "Submit" %></h3>
</div>
<% end %>
When no action is taken, the hidden field translates to <input type="hidden" name="time" id="time" value="{:id=>"q1-hf"}"> in html, which may or may not be a problem.
And have tried several ways to replace the console.log statement in the javascript and assign a value to the hidden field based on which div is .active.
First, document.getElementById("q1-hf").value = 1; which receives a console error saying new:200 Uncaught TypeError: Cannot set property 'value' of null
Alternatively, $('#q1-hf').val(1); generates no console errors, but also does not change the hidden_field_tag value.
Can anyone help me get this working? I'm open to other structures that accomplish the same goal, but I have looked at other posts like this or this that seem to say this should be possible.
<%= hidden_field_tag :time, id: "q1-hf" %>
This code is making a hidden input with id="time", so you should select it by document.getElementById("time")
If you want the id to be "q1-hf", then you should change it to
<%= hidden_field_tag 'q1-hf' %>
It will be rendered as <input name="q1-hf" id="q1-hf" type="hidden"> and you can access it the way you described in your question.

Rails showing series of objects with form_for and Ajax

I'm trying to show a list of "Interests" that users can "follow" by clicking on a button associated with the Interest's image. Upon clicking the button to follow an Interest, the Interest (and it's corresponding image) should disappear from the list.
I'm using a hide function in a create.js.erb file called upon by a Relationship controller when the follow button is clicked, but the function will only hide the first Interest in the series of Interests--NOT the Interest the user clicked on that needs to disappear. So there has to be a better way to set this up, but I cannot, for the life of me, figure it out. Here's my current setup:
My Controller
class StaticPagesController < ApplicationController
def home
#user = current_user
#city = request.location.city
#interests = Interest.all
end
The home page
<%= render #interests %>
The partial
<div id="follow_form">
<div class="four columns portfolio-item interests">
<div class="our-work">
<a class="img-overlay">
<%= image_tag interest.image_path %>
<div class="img-overlay-div">
<h4><%= interest.name %></h4>
</br>
<h5>Placeholder
</br>
<%= interest.desc %></h5>
<%= form_for(current_user.relationships.build(followed_id:
interest.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "I'm interested", class: "b-black" %>
<% end %>
</div>
</a>
<h3><%= interest.name %><span>features info</span></h3>
</div>
</div>
</div>
And the create.js.erb that's called upon by a Relationships controller when you click the button:
$("#follow_form").hide();
UPDATE
Here is the Relationships controller for additional clarification
def create
#interest = Interest.find(params[:relationship][:followed_id])
current_user.follow!(#interest)
respond_to do |format|
format.html { redirect_to(root_url) }
format.js
end
end
You need to provide something to differentiate the different "follow_forms" that you have on the same page. Otherwise, jQuery will just select the first element and only remove that one.
One idea of how to do this would be to append the "interest.id" to the end of the follow_form id
Update your partial to be like this (assuming your partial is called and located in /app/views/interests/_follow_form.html.erb:
<div id="follow_form<%="#{interest.id} %>">
<div class="four columns portfolio-item interests">
<div class="our-work">
<a class="img-overlay">
<%= image_tag interest.image_path %>
<div class="img-overlay-div">
<h4><%= interest.name %></h4>
</br>
<h5>Placeholder
</br>
<%= interest.desc %></h5>
<%= form_for(current_user.relationships.build(followed_id:
interest.id), remote: true) do |f| %>
<div><%= f.hidden_field :followed_id %></div>
<%= f.submit "I'm interested", class: "b-black" %>
<% end %>
</div>
</a>
<h3><%= interest.name %><span>features info</span></h3>
</div>
</div>
</div>
This way each follow_form will have a unique ID attached to it. Then you can loop through your interests and call a partial on each one like this:
home page:
<% #interests.each do |interest| %>
<%= render 'interests/follow_form', :interest => interest %>
<% end %>
create.js.erb
$("#follow_form_<%= "#{#interest.id}" %>").hide())
Your controller action can stay the same.

Rails 3 coffeescript controller association?

Alright, I'm a JS / JQuery / Coffeescript noob. This is probably easy points for someone.
Having successfully implemented RBate's Nested Form Model railscast, I am attempting to reproduce this in a simpler model: Chapters have many counties.
I have a chapters.js.coffee file with the following code:
jQuery ->
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('#destroy').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
This code works just fine in the other model. But not here.
_chapters_form.html.erb:
<div class="row span12">
<%= form_for(#chapter) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="span2"><strong>Chapter name:</strong></div>
<div class="span6"><%= f.text_field :name %></div>
<div class="span2"><strong>Chapter Number:</strong></div>
<%= f.number_field :chapter_num, class: "span2" %>
</div>
<div class="row span12">
<div class="span12">
<%= f.fields_for :counties do |builder| %>
<%= render 'county_fields', f: builder %>
<% end %>
</div>
<% if f.object.new_record? then link = 'Add the Chapter' else link = 'Update Chapter' end %>
<%= f.submit "#{link}", class: "btn btn-large btn-primary" %>
<%= link_to "Cancel", chapters_path, class: "btn btn-large btn-primary" %>
<% end %>
</div>
and:
_county_fields.html.erb:
<fieldset>
<div class="well span12">
<div class="row span12">
<div class="span3">County Number: <br /><i>(6-digit FIPS code)</i></div>
<div class="span2"><%= f.number_field :county_num %></div>
<div class="span2">County Name:</div>
<div class="span5"><%= f.text_field :name %></div>
</div>
<div class="row span12"><hr></div>
<div class="row span12">
<div class="span6">Move to new Chapter:</div>
<div class="span6"><%= select(:county, :chapter_id, Chapter.all.collect {|c| [c.name, c.id]}) %></div>
</div>
<div class="row span12">
<div class="pull-right">
<%= f.hidden_field :_destroy, id: "destroy" %>
<%= link_to "remove county", "#", class: "remove_fields" %>
</div>
</div>
</div>
</fieldset>
There are no errors in the JS. Again, noob speaking, but it doesn't seem that the JS is getting called. Clicking <%= link_to "remove county", "#", class: "remove_fields" %> just adds the # to the URI.
What am I doing wrong?
As requested, the HTML in a fiddle which doesn't work either.
Your HTML is broken. You open a div before the form element, then close it before you close the form. If you move the form element up to just inside the container div, it works.
<body>
<div class="container-fluid">
<form accept-charset="UTF-8" action="/chapters/7" class="edit_chapter" id="edit_chapter_7" method="post">
<div class="row-fluid">
...
</div>
</form>
</div>
</body>
You should take more care in the indenting of your HTML to help avoid this sort of simple mistake. Code format matters.
The page on which they appear - regardless of how they are rendered - must be an action of the chapters_controller for chapters.js.coffee to be included. My bet is that the script is not being included at all, as the code looks fine. Check out the pages you are having issues with with this code:
jQuery ->
console.log "included chapters.js.coffee"
$('form').on 'click', '.remove_fields', (event) ->
console.log "clicked .remove_fields"
$(this).prev('#destroy').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
Also, post up the rendered HTML in your question

Is it possible to populate a javascript attribute from a records field?

I have a table of venue records which are being displayed on the index page as partials. Is it possible for the .left and .top values in the javascript to be populated by an integer field taken from venue records?
Also, how can I have the script shown run for each partial as it currently only applies to the first partial generated.
Venue partial:
<%= link_to venue do %>
<div class="venue_partial">
<div class="venue_icon">
<%= image_tag venue.venuetype.photo.url(:thumb), :class => 'image' %>
</div>
<span class="venue_partial_name"><%= venue.name %></span>
<span class="venue_area_and_type"><%= venue.venuetype.name %></span>
<span class="venue_area_and_type"><%= venue.area.name %></span>
</div>
<div id="venue_map_icon" style="position:absolute;"></div>
<script>
document.getElementById("venue_map_icon").style.left= "300px";
document.getElementById("venue_map_icon").style.top= "310px";
</script>
<% end %>
Thanks very much for any help its much appreciated!
Yes on both counts. The problem you have now is that you are referencing an element by an ID in javascript, but that ID is not unique, so javascript finds the first instance of this ID and applies the rules to it, not to the rest of your divs. To fix this, you need to use unique ids:
<%= link_to venue do %>
<div class="venue_partial">
<div class="venue_icon">
<%= image_tag venue.venuetype.photo.url(:thumb), :class => 'image' %>
</div>
<span class="venue_partial_name"><%= venue.name %></span>
<span class="venue_area_and_type"><%= venue.venuetype.name %></span>
<span class="venue_area_and_type"><%= venue.area.name %></span>
</div>
<div id="venue_map_icon_<%= venue.id %>" style="position:absolute;"></div>
<script>
document.getElementById("venue_map_icon_<%= venue.id %>").style.left= "<%= venue.left %>px";
document.getElementById("venue_map_icon_<%= venue.id %>").style.top= "<%= venue.top %>px";
</script>
<% end %>
Sure, it just needs to be output just like you do in the HTML:
document.getElementById("venue_map_icon").style.left = "<%= venue.somevalue %>px";

Categories