rails crud using jquery popover via bootstrap - javascript

I tried to add jquery popover to rails default cruds instead of redireting to views and what I am doing is rendering the form in the jquery popover:
<%= content_tag_for :tr, #order.order_items do |i| %>
<a class='btn btn-mini label-with-popover' id=<%=i.id%>><%= t('helpers.links.edit_html') %> </a>
<% end %>
<script type="text/javascript">
$(function () {
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item') %>" ,
placement: 'top'
} );
});
</script>
and here is my form_item:
<%= simple_form_for :order_item, url: admin_shop_order_path, :html => { :class => 'form-horizontal' } do |f| %>
<div class='form-inputs'>
<%= f.input :item_id , :collection => #shop.products.collect{|b| b.variants}.flatten.map{|variant| ["#{variant_full_title(variant)}", variant.id]}%>
<%= f.input :quantity %>
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn-primary' %>
<%= link_to t("helpers.links.cancel"), admin_shop_orders_path, :class => 'btn' %>
</div>
<% end %>
but the problem appears on the edit button. to render the edit form we need the object that we want to edit(:order_item I mean) and a way to get that is by using the id and this is why I have set the anchor tag id. now we have to get that anchor id within the popover function but $(this).id doesn't work. any suggestion?

In jQuery, you need to use attr('id') to get the id of an element. Try to replace $(this).id with:
$(this).attr('id')
See jQuery's documentation for more details: http://api.jquery.com/attr/
But a prefered way to achieve this is usually to use data attributes. It's a clean way to pass data from your view to some JS code. Your link would look like this:
<a class='btn btn-mini label-with-popover' data-item-id=<%=i.id%>>...</a>
And in your JS file, you would get this value using:
$(this).data('item-id')
jQuery's documentation: http://api.jquery.com/data/

You are rendering wrongly, I think, you should try to render your partial with locals. You can also pass local variables into partials, making them even more powerful and flexible.
In your case, while rendering your partial form_item in script tag, so you can write it like this:
<script type="text/javascript">
$(function () {
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item', :locals => {order_item: #order_item}) %>" ,
placement: 'top'
} );
});
</script>
and in your form you will be able to access it like :
<%= form_for(order_item) do %>
# write your form stuff %>
<% end %>
This way you can handle your form for both create or edit operations.
First I suggest you to pass id with some text added to it(I replaced the id with "link_<%= i.id%>").
Second call a onclick function on your a tag:
<%= content_tag_for :tr, #order.order_items do |i| %>
<a class='btn btn-mini label-with-popover' id="link_<%=i.id%>" ><%= t('helpers.links.edit_html') onclick="javascript:openPopup(this.id)" %> </a>
<% end %>
and Last but not the least, get id in your function, and pass it through your partial.
<script type="text/javascript">
function openPopup(a_id) {
var id = a_id.split("link_")[1];
$('.label-with-popover').popover({
html : true,
content: "<%= escape_javascript(render :partial => 'form_item', locals => {:id => id}) %>" ,
placement: 'top'
} );
});
</script>
I am not good at javascript, but from my answer, you will get, what I wanted to explain you. and I am sure that you will find a more improved way to do this. And If you do, please post it here as well. Hope it will help. Thanks

Related

Rails 6: execute a JS function after rendering partial

Rails & Javascript beginner here,
On a training project, I made flash messages disappear after few seconds using JQuery. A visitor would send AJAX request to add a product to his cart, then a flash partial 'Added to cart' appears and automatically fades out after few seconds.
# application.html.erb
<div id='flash' class='flex-column'>
<%= render partial: 'shared/flash' %>
</div>
# shared/_flash.html.erb
<% flash.each do |key, value| %>
<%= display_flash(key, value) %>
<%= javascript_pack_tag 'custom/flash' %>
# this works, but injects the script each times the partial is rendered
<% end %>
# helpers/application_helper.rb
def display_flash(key, value)
def display_flash(key, value)
div_class = [flash_class(key), 'text-center fade show alert-dismissible'].join(' ')
content_tag(:div, class: div_class) do
content_tag(:p, value) +
button_tag(class: 'ml-auto close', 'data-dismiss': 'alert', type: 'button') do
content_tag(:span, '×'.html_safe, 'aria-hidden': 'true') +
content_tag(:span, 'Close', class: 'sr-only')
end
end
end
end
// app/javascript/packs/custom/flash.js
function closeFlash(){
let lastAlert = $('#flash .alert:last')
function fadeFlash() {
lastAlert.animate( {opacity: 0}, 2000, function() {
$(this).hide('slow', function() {
$(this).remove()
});
});
};
setTimeout(fadeFlash, 2000)
};
closeFlash();
The issue with this is that it pollutes my DOM with unnecessary <script> tags:
This could be fixed, but is there a suitable way to execute one specific javascript function after rendering a (AJAX) partial ?
In my case, executing closeFlash() located in packs/custom/flash.js each time a partial is rendered.
Thanks for your help and your time
EDIT Solution:
From Amit Patel answer and this post
# app/views/shared/_flash.html.erb
<% flash.each do |key, value| %>
<%= display_flash(key, value) %>
<script>
$(function() {
closeFlash();
});
</script>
<% end %>
// app/javascript/packs/custom/flash.js
window.closeFlash = function() {
let lastAlert = $('#flash .alert:last')
function fadeFlash() {
lastAlert.animate( {opacity: 0}, 2000, function() {
$(this).hide('slow', function() {
$(this).remove()
});
});
};
setTimeout(fadeFlash, 2000)
};
It doesn't inject the whole function but (I believe) the minimal javascript code to call it.
Move <%= javascript_pack_tag 'custom/flash' %> to your layout or your application.js` so that it is available across the app.
modify your template like
application.html.erb
<div id='flash' class='flex-column'>
<%= render partial: 'shared/flash' %>
<script>
$(function(){
closeFlash();
});
</script>
</div>
A more recent approach would be to set a Stimulus controller.

Ruby on Rails - Pass item selected from a Javascript dropdown to Rails controller when a button is clicked

I have a button right next to a dropdown
When a user selects a value from the dropdown and clicks on the button, the value from the dropdown should be passed as a parameter to the Rails controller function
This is the code that I'm using:
<%= link_to 'Generate Report', forecast_report_pdf_path(format: :pdf), onclick: "GetSelectedItem('select1')", class: 'btn btn-primary' %>
how do I pass the value returned by the GetSelectedItem function to my controller? (I already pass format = pdf as a parameter now; but I am not sure how to pass the value returned by GetSelectedItem as well)
I don't if this is the best solution but you could use a form inside the dropdown so you select the option and the value is sent to the controller.
Here you have an example but feel free to adapt it to your code:
<%= form_for :pdf, url: forecast_report_pdf_path(format: :pdf) |form| %>
<%= select_tag(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>
<%= form.text_field :name %>
<%= form.submit 'Generate Report', class: 'btn btn-primary'%>
<% end %>
Check rails documentation for more details: https://guides.rubyonrails.org/form_helpers.html
<%= form_tag forecast_report_pdf_path do %>
<%= select_tag(:report_id, options_for_select([["Summary1", 7], ["Summary2", 6]])) %>
<%= hidden_field_tag :format, :pdf %>
<%= submit_tag("Generate Report") %>
I finally got around to writing the above code and it worked!! Thanks much for your inputs #nicolasnisoria !

Individual JavaScript for multiple records in Rails

I've been trying to wrap my head around this for days now, but I can't seem to find a solution for this problem.
Basically what I want to do is a feed similar to Facebook. A feed with multiple posts, of which each has comments/replies.
Now what I can't get to work is an AJAX "Load more" button for each post. I've only got it working for all posts at once, but not for individual ones.
What I have so far:
The feed:
<% #posts.each do |post| %>
<%= render "posts/thread", post: post %>
<% end %>
The threads:
<%= render post %>
<% replies = post.replies.paginate(:page => params["replies#{post.id.to_s}"], :per_page => 2) %>
<%= render replies %>
<%= will_paginate replies, :param_name => 'replies'+post.id.to_s %>
<%= link_to "Load More", "#", class: "load-more", id: post.id, :remote => true %>
The posts:
<div class="post" data-id="<%= post.id %>">
## content ##
</div>
pagination.coffee:
jQuery ->
$('.load-more').on 'click', ->
postId = $(this).attr('id')
more_posts_url = $('#post-'+postId+' .pagination .next_page').attr('href')
if more_posts_url
$('.load-more').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />')
$.getScript more_posts_url
return
return
index.js.erb:
<%= #posts.each do |post| %>
$('#post-<%= post.id %>').append('<%= j render post.replies %>');
<% end %>
But this does nothing.
I really don't understand how JS works for multiple records on one page.
I suppose this could help: https://jsfiddle.net/se708oou/2/
Main points of difference:
You should loop through all of the .load-more elements
$('.load-more').each(function() {
Change reference to use this
$(this).on('click', function() {
(Miscellaneous) This is how you take data-* in jQuery:
postId = $(this).data('id');
Cheers! :)
Your posts div
<div class="post" data-id="<%= post.id %>">
...
</div>
Has a class of post and a data attribute id, but in index.js.erb you are trying to select #post-<%= post.id %>
That selector isn't in the dom, You need to change your selector to
$('.post[data-id=<%= post.id %>').append('<%= j render post.replies %>');

If you can't nest HTML forms, how can you submit multiple selections?

So for example, I have a table with a checkbox for each row. Now, I added in the functionality to be able to delete multiple selected rows by using built in rails form helper. The problem is, I also need to be able to 'disable selected' and 'enable selected' rows as well.
<%= form_tag sccm_destroy_multiple_url , method: :delete do %>
.... some table rows and data
<%= check_box_tag "ID[]", group.id %>
... some more rows
<%= button_tag "Delete Selected", type: 'button submit', class: "btn btn-danger",data: {confirm: "Are you sure you want to delete these groups?"} do %>
<i class="fa fa-trash-o"></i> Delete Selected
<%end%>
<%end%>
Now I need to put another button to "Disable Selected" but I can't do this since it would be nesting forms and forms are the only way to store multiple checkbox IDs that I'm aware of...any ideas?
Basically rails gets around this limitation in HTML by creating arrays and nested hashes from the name attributes:
<input type="text" name="dogs[0][breed]" value="Husky">
<input type="text" name="dogs[1][breed]" value="Shiba Inu">
rails will interpret these params as:
"dogs" => {
"0" => {
"breed" => "Husky"
},
"1" => {
"breed" => "Shiba Inu"
},
}
You would normally use fields_for which gives you a special "scoped" form builder instance:
<%= forms_for(#adaption_agency) do |k| %>
<%= f.fields_for(:dogs) do |doggy_fields| %>
<%= doggy_fields.text_field :breed %>
<% end %>
<% end %>
In your case what you could create an update_multiple route and use fields_for with an array of records.
But I would consider using ajax instead to allow the user to manipulate several records on the same page with just the standard CRUD actions. It is a lot less complex than trying to mosh everything into mega actions.
<%= dogs.each do |doggy| %>
<%= form_for(doggy), class: 'doggy-form' do |f| %>
<%= f.text_input :breed %>
<% end %>
<%= button_to('Delete', dog_path(doggy), method: :delete, class: 'doggy-form') %>
<% end %>
Note that this creates forms for each type of action.

Have a form submit to two possible different controllers

I have the following form on a site
<% form_tag({ :action => :subid}, :id=>'run_report', :name => 'run_report') do %>
Beginning Date <%= date_select("post", "start_date", :start_year => 2010,:order => [:month, :day, :year]) %><br />
End Date <%= date_select("post", "end_date", :start_year => 2010,:order => [:month, :day, :year]) %><br />
<%= submit_tag 'Run', :name => "submit", :class => "submit_btn" %>
<% end %>
I want to add another button called Generate that is passed the same variables (start_date and end_date) as the form below but calls the action generate instead of run. Ideally I'd like to do this without having to have two duplicate forms.
Any help is appreciated.
or you may bind your button onclick action Something like
$("#your_button_id").click(function() {
$.post("second_url", $("#your_form_id").serialize());
return true;
});
It's possible with a javascript. In the view you can use name attribute or html5 data-attribute as I do below. The main idea is that it's necessary to store somewhere url to proper form action.
<% form_tag(:id=>'run_report', :name => 'run_report') do %>
<%= submit_tag 'Run', :name => "submit", :data-action => "#{url_for(action1)}", :class => "submit_btn" %>
<%= submit_tag 'Generate', :name => "generate", :data-action => "#{url_for(action2)}", :class => "submit_btn" %>
<% end %>
Simple JQuery script (should work but not tested):
var $form = $('#run_report);
$('#run_report :submit').click(function() {
$form.attr('action', $(this).attr('data-action')).addClass('ready').submit();
});
$form.submit(function(e){
if(!$form.hasClass('ready')) {
e.preventDefault();
});
The trick is that while form has not class ready, it won't be submitted. And you add this class after setting proper form action.
This works for me when I initialize hidden form fields with values depending on what button was clicked but with the same form action. Though the approach in your case can be the same. Hope this direction will help you.

Categories