I have next file with jquery function:
assets/ javascripts/poll_items.js
$(document).ready(function(){
$('.up_id').on('click', function() {
$(this).closest('.poll_row').insertAfter($(this).closest('.poll_row').next());
});
$('.down_id').on('click', function() {
$(this).closest('.poll_row').insertBefore($(this).closest('.poll_row').prev());
});
});
when i click `= button_tag 'up', type: 'button', value: 'up', input_html: {class: 'up_id'}' nothing happens. How me correct call code from file javascript?
full code:
polls/new.html.haml
%h1= title "Новый опрос"
= simple_form_for #poll do |f|
= f.error_messages header_message: nil
= f.input :question, disabled: !#poll.editable?(current_user), input_html: { class: 'input-block-level' }
= f.input :results_hidden, as: :boolean, inline_label: 'Скрыть результаты до окончания опроса', label: false
= f.input :from_date, as: :datetime, input_html: { class: 'poll_date' }
= f.input :to_date, as: :datetime, input_html: { class: 'poll_date' }
%h3#poll-items Варианты ответа (не больше пяти)
.item_index
= f.simple_fields_for :poll_items do |poll|
= render 'poll_item_fields', f: poll
= link_to_add_association 'Добавить еще вариант', f, :poll_items
.form-actions
= f.button :submit, 'Опубликовать опрос', class: 'btn-bg'
%p
Вернуться к посту:
= link_to #owner
poll_fields.html.haml
%h3#poll-items Варианты ответа (не больше пяти)
.item_index
= f.fields_for :poll_items do |poll|
= render "poll_item_fields", f: poll
.links
= link_to_add_association 'Добавить еще вариант', f, :poll_items, render_options: {class: 'links'}
poll_item_fields.html.haml
.poll_row
.poll_item
= f.input :answer, input_html: { class: 'ctrlenter expanding' }, label: false, placeholder: 'Введите вариант ответа'
= button_tag 'up', type: 'button', class: 'up_id', value: 'up'
= button_tag 'down', type: 'button', class: 'down_id', value: 'down'
= link_to_remove_association "удалить", f, { wrapper_class: 'poll_item' }
I am suspecting that the DOM is getting build dynamically so try changing your JS code as follows:
$(document).ready(function(){
$(document).on('click', '.up_id', function() {
$(this).closest('.poll_row').insertAfter($(this).closest('.poll_row').next());
});
$(document).on('click','.down_id', function() {
$(this).closest('.poll_row').insertBefore($(this).closest('.poll_row').prev());
});
});
Here we are using event delegation technique to propagate the click event to required DOM element which may be present in DOM.
SOLUTION:
I added in application.js.coffee #=require poll_items.js and everything works
Related
I'm using the cocoon gem on my rails app and I have two nested fields inside a form (categories, subcategories). Initially I'm showing the first one only and the second one is hidden. Each time the first select fields has subcategories the second field gets populated and appears.
The nested fields:
<div class="nested-fields">
<%= form.input :category, collection: #categories, as: :grouped_select, group_method: :children, :label => false, :include_blank => true, input_html: { id: "first_dropdown" } %>
<div class="show_hide">
<%= form.input :subcategories, label: false, :collection => [], :label_method => :name, :value_method => :id, required: true, input_html: { multiple: true, id: "second_dropdown" } %>
</div>
<div class="links">
<%= link_to_remove_association "Remove", form %>
</div>
</div>
This is the code to initially hide the second field
$('#first_dropdown').keyup(function() {
if ($(this).val().length == 0) {
$('.show_hide').hide();
}
}).keyup();
This is the code to show the second select input when the first select input has subcategories:
def select_item
category = Category.find params[:category_id]
render json: category.children
end
$('#first_dropdown').on('change', function() {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data) {
let childElement = $('#second_dropdown');
if( data.length === 0 ) {
$('.show_hide').hide();
} else {
$('.show_hide').show();
}
childElement.html('');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
childElement.append('<option value="' + id + '">' + name + '</option>');
});
}
});
});
Everything works well for the initial opened field. But when I add more fields and select a value inside any of the first select fields it populates all of the second fields according to that value. I think it's because I'm using specific id's for this and when I add more fields, all of them end up having the same id's. How can I set this up so I properly populate the second select field each time I add more nested fields to the form?
I'd give your selects classes instead of ids:
<div class="nested-fields">
<%= form.input :category, collection: #categories, as: :grouped_select, group_method: :children,
label: false, include_blank: true, input_html: { class: "first_dropdown" } %>
<% if f.object.category && f.object.category.sub_categories.length > 0 %>
<div class="show_hide">
<%= form.input :subcategories, label: false, collection: form.object.category.subcategories, label_method: :name,
value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
</div>
<% else %>
<div class="show_hide" style="display: none;">
<%= form.input :subcategories, label: false, collection: [], label_method: :name,
value_method: :id, required: true, input_html: { multiple: true, class: "second_dropdown" } %>
</div>
<% end %>
<div class="links">
<%= link_to_remove_association "Remove", form %>
</div>
</div>
then find the second select relative to the first one adding this to a page specific js file:
$(document).on('turbolinks:load cocoon:after-insert', function(){
$('.first_dropdown').off('change').on('change', function(){
let $this = $(this);
let $second = $this.closest('.nested-fields').find('.second_dropdown');
$second.children().remove();
$second.closest('.show_hide').hide();
if ($this.val() !== '') {
$.ajax({
url: 'categories/select_item?category_id=' + $(this).val(),
dataType: 'json',
success: function(data){
if (data.length > 0) {
$second.append('<option value=""></option>');
data.forEach(function(v) {
let id = v.id;
let name = v.name;
$second.append('<option value="' + id + '">' + name + '</option>');
});
$second.closest('.show_hide').show();
}
}
})
}
});
});
So I have put together a comment section on my rails app from a couple tutorials online. Everything works well, except that when I post a comment, it doesn't get displayed unless I reload the page. I'm using the acts_as_commentable_with_threading gem.
Here's my comments controller:
class CommentsController < ApplicationController
before_action :authenticate_user!
def create
commentable = commentable_type.constantize.find(commentable_id)
#comment = Comment.build_from(commentable, current_user.id, body)
if #comment.save
make_child_comment
redirect_back fallback_location: root_path, :notice => 'Comment was successfully added.'
else
render :action => "new"
end
end
private
def comment_params
params.require(:comment).permit(:body, :commentable_id, :commentable_type, :comment_id)
end
def commentable_type
comment_params[:commentable_type]
end
def commentable_id
comment_params[:commentable_id]
end
def comment_id
comment_params[:comment_id]
end
def body
comment_params[:body]
end
def make_child_comment
return "" if comment_id.blank?
parent_comment = Comment.find comment_id
#comment.move_to_child_of(parent_comment)
end
end
Here are my view files.
_form partial:
.comment-form
= simple_form_for #new_comment, :remote => true do |f|
= f.input :commentable_id, :as => :hidden, :value => #new_comment.commentable_id
= f.input :commentable_type, :as => :hidden, :value => #new_comment.commentable_type
.field.form-group
= f.input :body, :input_html => { :rows => "2" }, :label => false
.field.form-group
= f.button :submit, :class => "btn btn-primary", :disable_with => "Submitting…"
_reply.html.haml
- comments.each do |comment|
.comments-show
.comment
.media.mb-4
.d-flex.mr-3
.float-left.image
- if comment.user.profile_image.present?
.review-img
= image_tag attachment_url(comment.user, :profile_image, :fit, 50, 50, format: "jpg")
- else
%img.review-img{:alt => "user profile image", :src => "https://img.icons8.com/bubbles/100/000000/user.png"}/
.media-body
.small
%b
- if comment.user.username.present?
{comment.user.username}
- else
{comment.user.full_name}
.small.text-muted
{time_ago_in_words(comment.created_at)} ago
.small
{content_tag(:div, comment.body, style: "white-space: pre-wrap;")}
%br
.comment-nav
%a.comment-reply{:href => "#/", class: "btn btn-sm btn-link"} reply
.reply-form
= simple_form_for #new_comment do |f|
= f.hidden_field :commentable_id, value: #new_comment.commentable_id
= f.hidden_field :commentable_type, value: #new_comment.commentable_type
= f.hidden_field :comment_id, value: comment.id
.field.form-group
= f.text_area :body, class: 'form-control'
.field.form-group{:style => "margin-bottom: 60px"}
= submit_tag "Post Reply", class: 'btn btn-primary', style: "float: right;"
%div{:style => "margin-left: 100px;"}
= render partial: "comments/reply", locals: {comments: comment.children}
_template.html.haml
.card.my-2
.scrollable
.card-body
%b
#{pluralize(commentable.comment_threads.count, "Comment")}
%hr
= render :partial => 'comments/form', :locals => { new_comment: new_comment }
= render partial: 'comments/reply', locals: {comments: commentable.root_comments}
comments.js.coffee
$ ->
$('.comment-reply').click ->
$(this).closest('.comment').find('.reply-form').toggle()
return
jQuery ->
$(".comment-form")
.on "ajax:beforeSend", (evt, xhr, settings) ->
$(this).find('textarea')
.addClass('uneditable-input')
.attr('disabled', 'disabled');
.on "ajax:success", (evt, data, status, xhr) ->
$(this).find('textarea')
.removeClass('uneditable-input')
.removeAttr('disabled', 'disabled')
.val('');
$(xhr.responseText).hide().insertAfter($(this)).show('slow')
$(xhr.responseText).hide().insertAfter($(this))
appends a div containing your xhr.responseText with inline style "display: none" after and returns no jquery handle at all.
$(xhr.responseText).hide().insertAfter($(this)).length
> 0
so select the new div as sibling of form element and cast show('slow')
$(this).siblings('div').show('slow')
I don't understand what I do wrong. Can you help me? I want to make the button disabled if user not select anything in the selected field.
What I have:
Button:
= f.submit t('etc.actions.send'), data: {style: 'expand-left'}, class: 'ladda-button btn btn-success'
Select input field:
= f.input :id, collection: [['None', nil]] + #company.lead_sources.order(:name).map{ |ls| [ls.name, ls.id] }, label: 'Assign Lead Source', class: 'abc'
view_file
.modal-body
.row.m-t-md
.col-xs-12.col-sm-12.col-md-12
= f.input :id, collection: [['None', nil]] + #company.lead_sources.order(:name).map{ |ls| [ls.name, ls.id] }, label: 'Assign Lead Source', class: 'abc'
.modal-footer
.actions
.pull-right
= link_to t('etc.actions.cancel'), '#', class: 'btn btn-default', data: {dismiss: 'modal'}
= f.submit t('etc.actions.send'), data: {style: 'expand-left'}, class: 'ladda-button btn btn-success'
javascript:
$('#abc').on('change', function () {
$('#ladda-button btn btn-success').prop('disabled', !$(this).val());
}).trigger('change');
Try this:
javascript:
$('.abc').on('change', function () {
$('.ladda-button').prop('disabled', !$(this).val());
}).trigger('change');
Explanation:
You are doing $('#abc') and $('#ladda-button'). # is used to find the elements by their id. For classes, you use .. In your HTML, 'abc', 'ladder-button', 'btn', and 'btn-success' are all classes.
You should make the button disabled by default:
$('.ladda-button').prop("disabled", true);
And then on change in the select input field, make it active:
$('.abc').on('change', function(){
$('.ladda-button').removeAttr('disabled');
});
How do js get the selected value in a dropdown and pass it to the controller so that it returns a list of names for the other dropdown?
What I've done so far:
salmonellas.js
$(function () {
$('body').on('change', "#mode_change", function () {
var selectedText = $(this).find("option:selected").text();
var selectedValue = $(this).val();
if (selectedText) {
$.get('/salmonellas/find_stages?mode_title='+ selectedText, function(selectedText) {
return $(selectedText).html();
});
}
});
});
salmonellas/form.html.erb
<div class="process_salmonellas">
<% f.simple_fields_for :process_salmonellas do |process_salmonella| %>
<%= render 'process_salmonella_fields', :f => process_salmonella %>
<% end %>
</div>
salmonellas/_process_salmonella_fields.html.erb
<div class="mode_change" id="mode_change">
<%= f.collection_select :title, Mode.order(:name), :id, :name, class: 'mode_change', id:'mode_change', include_blank: true, "data-content": :mode_id%>
</div>
<h4>Stage</h4>
<div class="stage">
<% f.simple_fields_for :stages do |stage| %>
<%= render 'stage_fields', :f => stage %>
<% end %>
</div>
salmonella/_stage_fields.html.erb
<div class="form-inputs">
<%= f.grouped_collection_select :title, Mode.order(:name), :steps, :name, :id, :name, class: "step_selection" %>
</div>
salmonellas_controller.rb
def find_stages
#mode = Mode.where("name = ?", params[:mode_title])
#steps = #mode.steps
end
Is looking for another model, why the fields have to be pre-registered. I'm using cocoon to let it nested.
Updating
salmonellas/_process_salmonella_fields.html.erb
<div class="form-inputs">
<%= f.collection_select :title, Mode.order(:name), :id, :name, class: 'mode_change', id:'mode_change', include_blank: true, "data-content": :mode_id%>
</div>
<h4>Stage</h4>
<div class="stage">
<% f.simple_fields_for :stages do |stage| %>
<%= render 'stage_fields', :f => stage %>
<% end %>
</div>
Updating 2
salmonellas/_process_salmonella_fields.html.erb
<div class="form-inputs">
<%= f.collection_select :title, Mode.order(:name), :id, :name, id:'mode_change', include_blank: true %>
</div>
salmonellas.js
$(function () {
$('body').on('change', ".mode_change", function () {
var selectedText = $(this).find("option:selected").text();
var selectedValue = $(this).val();
if (selectedText) {
$.get('/salmonellas/find_stages?mode_title='+ selectedText, function(selectedText) {
return $(selectedText).html();
});
}
});
});
Updating 3
salmonellas.js
$(function () {
$(document).on('change', "#mode_change", function () {
var selectedText = $(this).find("option:selected").text();
var selectedValue = $(this).val();
alert("Selected Text: " + selectedText);
});
});
IDs must be unique on your DOM. You are duplicating them for div and select tag. First change them and use this to get the value of selected option
$(document).on('change', ".mode_change", function () {
var selectedText = $(this).find("option:selected").text();
var selectedValue = $(this).val();
...
I'm using rails4.2.0
in my e-commerce site, when a user submit his payment type, I want to redirect outside website. The flow is below.
a user choose payment type
use click submit button
-- ajax (format js) --
redirect to outside website using post method
source of 2 and 3 are like ,
create.html.erb
<%= form_for(:user,:url => { controller: "settlements", action: "settlement"}, remote: true, html: {class: :settlement_form}) do |f| %>
<%= f.radio_button :settlement_type, 0 %>paypal
<%= f.radio_button :settlement_type, 1 %>credit card
<%= f.hidden_field :email, :value => #user.email %>
<%= f.hidden_field :fee_type, :value => #user.fee_type %>
<%= f.submit "Submit", data: { disable_with: "Please wait..." }, class: "btn btn-warning" %>
settlement_controller
def settlement
user = User.new(user_params)
if user.save
# parameters for outside website
#payment_params
else
render "new"
end
end
settlement.js.erb
var form = $('<form></form>',{id:"pay",action:'http://outside_ec_site_url/hoge',method:'POST'}).hide();
var body = $('redirect');
body.append(form);
form.append($('<input/>', {type: 'hidden', name: 'something', value: <%= #payment_params[:something] %>}));
form.append($('<input/>', {type: 'hidden', name: 'something', value: <%= #payment_params[:something] %>}))
form.submit();
I fixed this problem, but I want to know better method.
changed settlement.js.erb like,
$("#redirect").html("<%= escape_javascript(render :partial => 'pay' ) %>");
and created new file _pay.html.erb
<script>
$('<form/>', {id: 'paygent',action: "outside_website_url", method: "POST"})
.append($('<input/>', {type: 'hidden', name: 'something1', value: "<%= #payment_params[:somthing1] %>"}))
.append($('<input/>', {type: 'hidden', name: 'something2', value: "<%= #paygent_params[:something2] %>"}))
.appendTo(document.body)
.submit();
</script>
then, it works.
Do you know any other method? Any idea is appriciated.