Generate unique identifier in rails helper - javascript

I have this rails helper _fields_answer.html.erb
<fieldset>
<div class="row">
<div class="col-xs-9">
<%= answerf.text_area :content, placeholder: "Answer", class: "form-control" %>
</div>
<%= answerf.hidden_field :question_number, value: qn %>
<div class="col-xs-2 buttonProjDel">
<%= answerf.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields btn btn-danger btn-xs buttonProjDelBut" %>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<% st = Time.now.strftime("%I%M%S%p") %>
<%= answerf.text_field :scale, id: 'slf'+st , type: 'text',
data: {'slider-id' => 'scale',
'slider-min' => '0',
'slider-max' => '10',
'slider-step' => '1',
'slider-value' => '5' } %>
</div>
</div>
<script>
$('<%= '#slf'+st %>').slider()
</script>
</fieldset>
And I render this helper dynamically, but I just can have the slider in the first one. So I tried to had a different Id for each one, but I am not able to do it.
With the code above I always get the same Id, even using the Time.
Why is the time used in id always the same?
There is another way to do this? I want to have a slide for each answer.
Edit:
The code that renders the helper:
In projects.js.coffee
$(document).on 'click', 'form .add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
In application_helper.rb
def link_to_add_fields(name, f, association, locals)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render("fields_" + association.to_s.singularize, answerf: builder, qn: locals[:qn])
end
link_to(name, '#', class: "add_fields " + locals[:class], data: {id: id, fields: fields.gsub("\n", "")})
end

Your view rendering was done within a second! hence the reason you are seeing the same id value for all inputs.
The statement below will have the same value for 60 seconds.
st = Time.now.strftime("%I%M%S%p")
As it appears you want the slider to be on each input, I would recommend using either a data attribute or simply a css class. An example using css class would be:
<div class="row">
<div class="col-xs-12">
<%= answerf.text_field :scale, class: 'has-slider', type: 'text',
data: {'slider-id' => 'scale',
'slider-min' => '0',
'slider-max' => '10',
'slider-step' => '1',
'slider-value' => '5' } %>
</div>
</div>
Say we assign class has-slider, we could then use this class selector and execute slider() on the matched elements in javascript as:
# app/assets/javascripts/input_slider.js.coffee
$ ->
# Execute slider on all inputs with has-slider css class
$('input.has-slider').slider()
And, you seem to know why you have slider only on the first input; This is because all your inputs have same id value and DOM selection retrieves the first match. ids need to be unique within a document.
Update:
As you're adding input elements within the dom after page load, the $('input.has-slider').slider() call made on app/assets/javscripts/input_slider.js.coffee would not work. It would only work for elements already present in the DOM. In order to make it work for elements added after DOM load, you can use jQuery on() method.
You could trigger some custom event and call slider() method as follows:
# app/assets/javascripts/projects.js.coffee
$(document).on 'click', 'form .add_fields', (event) ->
...
$(this).before($(this).data('fields').replace(regexp, time)).trigger('show-slider')
..
Then you will listen on the show-slider event and call slider() as:
# app/assets/javascripts/input_slider.js.coffee
$ ->
# Execute slider on all inputs with has-slider css class
$('input.has-slider').slider()
$(document).on 'show-slider', (event) ->
$(input.has-slider').slider()
Hope this clears any confusion.

Related

Rails 5 ajax form: submit form on clicking one of the 2 radio buttons in Ajax

Note:
Similar questions exist but none of them resemble my case
I am making a small Ajax form in my rails views with yes or no radio buttons, I made this form with remote: true and it acts correctly as Ajax if I use a separate submit button (no page reload happens & form is passed to controller)
If I use Jquery to make the radio buttons themselves submit the form, then it submits but not in Ajax (but with a page reload)
So how to fix this to:
Submit form on clicking one of the 2 radio buttons in Ajax?
The two things I tried:
1) Make two different submit buttons & try to adjust the form value in the submit btn itself, if I make them as submit buttons, Ajax works well but the form doesn't get my required value
<div class="radio-button-btn-wrapper mr-2">
<%= f.submit t('yes'), recommended: true, class: "radio-button-btn-label-white background-green" %>
</div>
<div class="radio-button-btn-wrapper mr-2">
<%= f.submit t('no'), recommended: false, class: "radio-button-btn-label-white background-red" %>
</div>
2) Adjust the radio buttons to submit the form in Ajax manner (tried this but it didn't work)
<%= simple_form_for [...model names], remote: true do |f| %>
<div class="flex">
<div class="radio-button-btn-wrapper mr-2">
<%= label :recommended, t('yes'), class: "radio-button-btn-label white" %>
<%= f.radio_button :recommended, true, onclick: "$(this).closest('form').submit();", class: "radio-button-btn background-green" %>
</div>
<div class="radio-button-btn-wrapper">
<%= label :recommended, t('no'), class: "radio-button-btn-label white" %>
<%= f.radio_button :recommended, false, onclick: "$.ajax((this).closest('form').submit());", class: "radio-button-btn background-red" %>
</div>
</div>
What worked for me was adding a 'normal' submit button and set it to display none. Then add an eventListener that listen to a click on the radio button and if that happens, let it click on the hidden submit button.
<%= simple_form_for [...model names], remote: true do |f| %>
<div class="flex">
<div class="radio-button-btn-wrapper mr-2">
<%= label :recommended, t('yes'), class: "radio-button-btn-label white" %>
<%= f.radio_button :recommended, true, class: "radio-button-btn background-green" %>
</div>
<div class="radio-button-btn-wrapper">
<%= label :recommended, t('no'), class: "radio-button-btn-label white" %>
<%= f.radio_button :recommended, false, class: "radio-button-btn background-red" %>
</div>
<%= f.submit '', class: 'd-none', id: 'submitMyForm' %>
</div>
document.addEventListener('click', function(event){
if(event.target.classList.contains('radio-button-btn'){
document.querySelector('#submitMyForm').click();
}
});
Please look into the code below and try to adapt it for your case. This is working code I have tested in my Rails app.
The basic idea is instead of trying to make the radio buttons as submit buttons I employed the approach of a hidden form and then bind click events on radio buttons. In the button click handler I set the selected radio button's value as a hidden field value in hidden form and submit the hidden form. It gets submitted as an AJAX request and on controller-end handle the JS response.
Hope this helps.
routes.rb
root "welcome#index"
post "welcome_submit_form" => "welcome#submit_form", as: :welcome_submit_form
app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
def index
end
def submit_form
#received_radio_value = params[:selected_radio_value]
render file: "welcome/submit_form_response.js.haml"
end
end
app/views/welcome/index.html.haml
.my-container<
%div(style='display: none;')
= form_tag(welcome_submit_form_path, class: 'my-hidden-form', remote: true) do |f|
= hidden_field_tag "selected_radio_value", nil
= radio_button_tag 'favorite_color', 'red', false, class: 'my-radio-btn'
Red
= radio_button_tag 'favorite_color', 'blue', false, class: 'my-radio-btn'
Blue
.my-selected-radio-value(style='display: none')
app/views/welcome/submit_form_response.html.haml
%h2= "My Radio Value: #{#received_radio_value}"
app/views/welcome/submit_form_response.js.haml
- response_markup = escape_javascript(render( partial: "welcome/submit_form_response.html.haml" ))
:plain
var responseMarkup = "#{response_markup}";
var responseContainer = $('.my-selected-radio-value');
responseContainer.html(responseMarkup);
responseContainer.show();
app/assets/javascripts/welcome.js
(function ($) {
bindClickEventOnMyRadioBtn = function() {
$('.my-container .my-radio-btn').off('click').on('click', function(event) {
var selectedRadioVal = $(this).val();
var myHiddenForm = $('.my-container .my-hidden-form');
var radioValueField = myHiddenForm.find('input[name=selected_radio_value]');
radioValueField.val(selectedRadioVal);
myHiddenForm.submit();
})
}
}) (jQuery);
var ready;
ready = function() {
bindClickEventOnMyRadioBtn();
};
$(document).ready(ready);
After extensive searching for why this happens & how to fix it in the easiest way, I found this issue in Rails 5.2 & thanks to this solution from Padi we should use this code to make it work as expected:
// Get hold of the form object
form = document.querySelector('form');
// Use the Rails fire helper
Rails.fire(form, 'submit');

How to show a form on load

How to show form on load. Now I can see form fields on click of button (add keyskills). I want all function to be work together.On load i want to see the form and onclick 'add key skill' or 'remove key skill' it should add and remove(now on click of add or remove it is working, but not sees the form on load, only on click of 'add key skill' i can see the form)
$(function()
{
function check_to_hide_or_show_add_key_skill_link()
{
if ($('#key_skills .nested-fields:visible').length == 4) {
$('#key_skills .links a').hide();
} else {
$('#key_skills .links a').show();
}
}
$('#key_skills').on('cocoon:after-insert', function()
{
check_to_hide_or_show_add_key_skill_link();
});
$('#key_skills').on('cocoon:after-remove', function()
{
check_to_hide_or_show_add_key_skill_link();
});
check_to_hide_or_show_add_key_skill_link();
});
My form
Key Skills*
<div id="key_skills">
<%= f.simple_fields_for :key_skills do |key_skill| %>
<div class="nested-fields">
<div class="field">
<%= f.input :name , input_html: { class: 'form-control keySkill', required: true }%>
<%= f.input :relevant_experience, input_html: { class: 'form-control', required: true } %>
<%= link_to_remove_association "Remove Skill", f %>
</div>
</div>
<% end %>
<div class="links">
<%= link_to_add_association 'Add Key Skill', f, :key_skills, class: "links" %>
</div>
</div>
This is my current output now. On click of add key skill i will get form,but i want it on load)
Since you do not provide a lot of information, I have no idea what the name of the top-element is, but let us assume it is a Job and a job has many key_skills.
In your JobsController there is a create method, more or less as follows:
def create
#job = Job.new
end
if you want to immediately add a key-skill to fill in you can just write
def create
#job = Job.new
#job.key_skills.build
end
(which will add an empty key-skill to fill in).
Related issue on cocoon-repository: https://github.com/nathanvda/cocoon/issues/420

Cocoon data-association-insertion-node with a function

According https://github.com/nathanvda/cocoon#link_to_add_association you should be able to pass a function to data-association-insertion-node
I have tried this:
<%= link_to_add_association 'Add Timeslot', f, :timeslots, :data => {'association-insertion-node' => 'get_row()'} %>
And this:
<%= link_to_add_association 'Add Timeslot', f, :timeslots, :data => {'association-insertion-node' => 'get_row'} %>
And this (getting desperate):
<%= link_to_add_association 'Add Timeslot', f, :timeslots, :data => {'association-insertion-node' => 'function get_row(node){var row = "<tr></tr>";$("#table_body").append(row);return row;}'} %>
But none of them work.
Javascript:
function get_row(node){
var row = "<tr></tr>"
$("#table_body").append(row);
return row
}
I am trying to add a tr to a table and then append the nested timeslot form to the tr.
At the moment this is not possible what you are trying to do. When setting the data-association-insertion-method like this, it will just be a string in javascript, and not a reference to a method.
As documented in the README, you have to do the following (assuming you gave your links a class .add-timeslot):
$(document).on('turbolinks:load', function() {
$(".add-timeslot a").
data("association-insertion-method", 'append').
data("association-insertion-node", function(link){
return link.closest('.row').next('.row').find('.sub_tasks_form')
});
});
(the example is almost verbatim copied from documention for demonstration purposes only --you will have to fill in your get_row function)

How can I update the message inside the data-confirmation attribute using Jquery?

I have a polymorphic association with Resources & Flags in my rails app. Each Resource can be Flagged by a user.
On the Resource Edit Page, an Admin can delete multiple flags associated with each Resource via Jquery. (This works fine)
I am then trying to display the total number of flags remaining in an alert message using the data-confirmation attribute before a user updates a Resource.
For Example:
<%= f.submit "Update Resource", data: { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." } %>
For some reason, my jquery updates the entire div button VS the variable inside the data-confirmation attribute.
How can I directly target the variable inside the data-confirmation attribute using Jquery?
Models
class Resource
belongs_to :district
has_many :flags, as: :flaggable, :dependent => :destroy
end
class Flag
belongs_to :flaggable, polymorphic: true
end
Resource Controller
#edit page
def edit
#district = District.find_by_abbreviation(params[:district_id])
#resource = #district.resources.find(params[:id])
#flaggable = #resource
#flags = #flaggable.flags.all
end
Views
edit.html.erb - Resource Edit Page
###Renders all flags & allows a user to delete flags via Javascript.
<div id="flags">
<%= render :partial => "flags/flag", :collection => #flags %>
</div>
###Resource Form
<div>
<%= simple_form_for ([#district, #district_resource]) do |f| %>
<div id="counter>
###How can I directly target the variable inside my alert message/data-confirm?
<%= f.submit "Update Resource", data: { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." } %>
</div>
<% end %>
</div>
_flag.html.erb (partial)
<div id="<%= dom_id(flag) %>" class="flag">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">
<%= link_to '×'.html_safe, [#flaggable, flag], method: :delete, remote: true %>
</span>
</button>
<b>Flag Alert: </b> <%= flag.message %>
</div>
destroy.js.erb
###This works and removes flags
$('#flags #<%= dom_id(#flag) %>').remove();
###This doesn't update the actual data-confirm attribute message
newFlagCount = $('#flags .flag').size();
$("#counter input[data-confirm]").data("Reminder: You have #{#flaggable.flags.count} flag alerts remaining.");
Well, $("#counter") is selecting the <div id="counter"> div and .html() is changing the entire div content. So, your code is doing exactly what you are asking it to do. You should target the submit button inside your "counter" div and change the data-confirm attribute's text.
$("#counter input[data-confirm]").data("Reminder: You have #{#flaggable.flags.count} flag alerts remaining.");
Please note, that if you are changing the number of flags using javascript, the variable in the text will be the original value (#flaggable.flags.count). You would need to regex the digit in your attribute text and change it using javascript.
put your JSON into a variable, then assign it into the element attribute:
var data = { confirm: "Reminder: You have #{#flaggable.flags.count} flag alerts remaining." }
$("#counter input").attr("data-confirm",data.confirm);
Combined the answer below to solve the problem. Thanks everyone.
newFlagCount = $('#flags .flag').length;
var data = { confirm: "Reminder: You have " + newFlagCount + " flag alerts remaining." }
$("#counter input[data-confirm]").data('confirm', data.confirm)

how to make collection_select value as link_to variable

I have the ff:
app/views/reports/index.html.erb
<h1>Reports</h1>
<br>
<legend>Categories</legend>
<div class="row">
<div class="span5">
<ol>
<li><%= link_to 'COMMENDATION', commendations_path(format: 'pdf'), { id: 'commendations_click' } %></li>
<%= collection_select(nil,
:employee_id,
#employees,
:id,
:last_name,
{:prompt => "Select an Employee"},
{:id => 'employees_select'}) %>
<br>
<%= collection_select(nil,
:employee_movement_id,
#employeemovements,
:id,
:position,
{:prompt => "-"},
{:id => 'employee_movements_select'}) %>
<li><%= link_to 'REPORT2', '#' %></li>
<li><%= link_to 'REPORT3', '#' %></li>
</ol>
</div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('#employees_select').change(function() {
$.ajax({
url: "<%= update_employee_movements_path %>",
data: { employee_id : $('#employees_select').val() },
dataType: "script"
});
});
$('#commendations_click').click(function() {
$.ajax({
url: "<%= commendations_path %>",
data: {
employee_id : $('#employees_select').val(),
employee_movement_id : $('#employee_movements_select').val()
},
dataType: "script"
});
});
});
</script>
app/controllers/reports_controller
class ReportsController < ApplicationController
before_filter :authenticate_user!
# GET /reports
def index
#employees = Employee.all
#employeemovements = EmployeeMovement.distinct_positions
end
def update_employee_movements
if params[:employee_id]
#employeemovements = [].insert(0, "Select Employee Movement")
else
employee = Employee.find(params[:employee_id])
#employeemovements = employee.employee_movements.map{ |a| [a.position, a.id] }.insert(0, "Select Employee Movement")
end
end
def commendations
emdates = EmployeeMovement.last_2_dates_obtained(params[:employee_movement_id])
date_from = emdates[0].date_obtained
date_to = emdates.length == 1 ? nil : emdates[1].date_obtained
emp = Employee.find(params[:employee_id])
#commendations = case date_to.nil?
when true then emp.commendations.this_day_onwards(date_from)
else emp.commendations.within(date_from, date_to)
end
end
end
What I'm trying to do here is, I'm creating a page filled with links and drop down lists that will serve as a Reports center. The idea is, each link will be catered by a controller. Each controller will be responsible in showing my PDF in the browser (through ThinReports, if you're curious).
The #employees_select change event is used for changing the value of the #employee_movements_select collection_select.
Now my problem is, how can i capture the value of both #employees_select and #employee_movements_select and pass them to my commendations action?
I tested link_to by hardcoding values, and it works (code below)
<%= link_to 'COMMENDATION', commendations_path(employee_id: 1, employee_movement_id: 12, format: 'pdf') %>
However, If I use javascript to push the values to my commendations action through the 'click' event, my commendations action will be called twice, thus an error occurs because the params[:employee_id] in the action is now blank.
By the way, I need those values because my commendations action needs it so I can populate my PDF report template.
Please help. Thanks a lot in advance!
UPDATE 1
-> Updated link_to:
<%= link_to 'COMMENDATION', '#', { id: 'commendations_click' } %>
-> Removed dataType: "script" in #commendations_click event handler
-> Updated url: in #commendations_click event handler
url: <%= commendations_path(format: 'pdf') %>
UPDATE 2 (RESOLUTION)
I tweaked my javascript to look something like this:
$('#commendations_click').click(function() {
event.preventdefault();
window.location = "<%=j commendations_path(format: 'pdf') %>" + "?employee_id=" + $('#employees_select').val() + "&employee_movement_id=" + $('#employee_movements_select').val();
});
Works perfect now.
Two things come to mind. You can wrap the two select lists within a form element and just do a submit. Everything inside the form will be submitted to your server and you can process the request and handle the redirect on the server side. The other thing you can do is to handle the commendations click event on the client side using jquery or something. Just bind to the click event of that link, grab the values of the two select lists and do whatever you want with it. Remember, link_to just gets rendered as plain old html links on the view. For e.g.
link_to "Profile", profile_path(#profile) gets rendered as Profile

Categories

Resources