When creating a form using a cocoon gem, when inserting a new cocoon, I can detect an action by calling the "cocoon: after insert" callback.
And I use this to replace the element of my id to make use of another javascript function to calculate the percentage in the other field, entered by the user.
But when I reopen the form using the update action, the cocoons are redefined. And this does not cause the callback "cocoon: after insert". to reset the id's, how it works when inserting one by one
I wanted to know if there is a way to capture the callbacks when returning the form for editing. So I can detect this and redraw the new ID again
I use this code in "form.html.erb" to replace the id when it creates new instances of the cocoon
var i = 0;
$(document).on('cocoon:after-insert', function (e) {
i++;
var id_drop_down_product = 'id_drop_down_product' + i;
$(this).find("#id_drop_down_product").attr('id', id_drop_down_product);
});
I thought it would work, in building the form for editing
Here is my code that calls the cocoons, where everything is working too
<div class="form-group">
<%= form.fields_for :pedido_produtos do |builder| %>
<% render partial: "shared/pedido_produto_fields", locals: {f: builder} %>
<% end %>
<%= link_to_add_association('Produto', form, :pedido_produtos, class: "btn btn-outline-success") %>
</div>
<div class="nested-fields">
<div class="order_product">
<div class="form-group col-md-12">
<%= f.collection_select(:product_id, Product.all, :id, :nome, {:prompt => "Selecione um Product"}, {:class => 'form-control', onchange: "searchProduct($(this).val(),this.id);", id: "dropselectProduct", :required => true}) %>
<%= f.text_field :quantity, class: "form-control", id: "quantityproducty", :required => true, onchange: "update_discount($(this).val(),this.id);" %>
<%= f.text_field(:porcent, class: "form-control", id: "porcent", readonly: true) %>
</div>
<%= link_to_remove_association('Del.', f, class: "btn btn-outline-danger") %>
</div>
</div>
in the view, I have a field, to get the value of the product and the value of the percentage field, (which was selected by the user) and calculate the percentage of the new value of the product.
but in this view, you can insert multiple products, and perform this action.
Picture view example:
https://ibb.co/s3Z43yk
Related
I have a form
_form.html.erb
<%= simple_form_for(#exam) do |f| %>
<%= f.error_notification %>
<div class="field">
<%= f.label :Year %>
<%= f.collection_select :year_id, Year.order(:name), :id, :name, prompt: true %>
</div>
<div class="form-inputs">
<%= f.input :marks_secured %>
</div>
<div class="form-actions">
<%= f.button :submit, "Submit" %>
</div>
<% end %>
And, every time the select box changes (i.e. a different year is selected), I want to display the list of students for the selected year. The list must be shown within the same page.
I don't know how to grab the ID of the selected item and use it back inside the view/controller in order to achieve this..
Here are my models:
student.rb
class Student < ApplicationRecord
belongs_to :year
has_many :exams
end
exam.rb
class Exam < ApplicationRecord
belongs_to :year
belongs_to :student
end
year.rb
class Year < ApplicationRecord
has_many :students
has_many :exams
end
I recommend using ajax in order to show the list of students every time the select option is changed; to do so you need to make several changes.
First, you need to determine where in your view you would like to display de students, for example, try adding a div in your form:
<%= simple_form_for(#exam) do |f| %>
<%= f.error_notification %>
<div class="field">
<%= f.label :Year %>
<%= f.collection_select :year_id, Year.order(:name), :id, :name, prompt: true %>
</div>
<div id="students"><!-- Students will be listed here --></div>
<div class="form-inputs">
<%= f.input :marks_secured %>
</div>
<div class="form-actions">
<%= f.button :submit, "Submit" %>
</div>
<% end %>
Next, add a script (on the same view where your form is) to detect when the select changes, and update (via ajax) the students list:
<script type="text/javascript">
$(function () {
function getStudents() {
var year = $('#exam_year_id').val();
$.ajax({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
type: "GET",
url: "<%= students_url %>",
data: { year: year },
success: function(response) {
$('#students').html(html);
}
});
}
$('#exam_year_id').on('change', getStudents );
getStudents();
});
</script>
In this example the function getStudents is called whenever the select changes ($('#exam_year_id').on('change', getStudents );) and after the page loads (getStudents();), so the students from the default year selected (that is, when your page first loads) get listed.
Also notice that we are replacing the content of the div recently added with a variable called html which will be delivered within a view by your students controller (next step).
Now you need to update your students controller to get the list of students for the selected year:
def index
#Students = Student.where(year: params[:year])
end
And create your view with the list of students:
var html = "<select name='exam[student_id]' id='exam_student_id'>";
<% #Students.each do |student| %>
html = html + "<option value='<%= student.id %>'><%= student.name %></option>"
<% end %>
html = html + "</select>";
The view must be named index.js.erb (instead of a index.html.erb) and will contain javascript; in this example it only contains a the variable (html) with the list of students (the list is created as another select box but you can change it).
Finally, in your exams controller add an update action that will update the exam's details:
def update
exam = Exam.find(params[:id])
exam.student_id = exam_params[:student_id]
exam.year_id = exam_params[:year_id]
exam.save!
end
private
def exam_params
params.require(:exam).permit(:year_id, :student_id)
end
And that's it! Hope this helps.
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.
I have a form element which is being pulled in on request with ajax. I am then trying to perform an ajax request on the inserted text box to find a location as it is typed. The code works on the first textbox but simply fails when the second one is inserted. I've tried to get the script to reload itself when the ajax has completed but it still won't work. Help would be much appreciated.
Form.html.erb - Sets up the rails nested form and pulls in partial
<%= nested_form_for(#post, :html=> {:multipart => true, :class=> "new_blog_post", :id=> "new_blog_post"}) do |f| %>
...
<fieldset>
<%= render :partial => 'search_locations', :locals => { :f => f } %>
</fieldset>
<p><%= f.link_to_add "Add a location", :locations %></p>
...
<% end %>
partial.html.erb - Pulled in on page load and then when 'Add a location' button is pressed
<fieldset>
<%= f.fields_for :locations do |m| %>
<%= m.text_field :name ,:class=>"localename", :placeholder=> "Name of the location", :autocomplete => "off" %>
<%= m.text_field :longitude, :class => "long" %>
<%= m.text_field :latitude, :class => "lat" %>
<div class="latlong">
<p class="help-block">Enter the name of the town or city visited in this blog entry.</p>
</div>
<%= m.link_to_remove "Remove this location" %>
<% end %>
</fieldset>
Javascript (placed at bottom of form)
<script type="text/javascript">
function locationchecker() {
// Rails to multiply the script 20 times
<% (0..20).each do |i| %>
// when the #search field changes
$(".localename:eq(<%=i%>)").keyup(function() {
// get the value of searchfield
var location<%=i%> = $(".localename:eq(<%=i%>)").val();
//Take the value of the textbox and pull it from geocoder
$.get('/locations/location?location='+location<%=i%>,this.value, function(searchone<%=i%>) {
$(".latlong:eq(<%=i%>)").html(searchone<%=i%>);
})
// Upon complete run the script again
.complete(function(searchone<%=i%>) { locationchecker });
});
<%end%>
}
// load script on doc ready
$(document).ready(locationchecker);
</script>
Help would be great!
Thanks in advance,
James
you should use the .on()(jQuery api doc) method to attach your keyup event handler, like so :
$('#myForm').on('keyup','.localename',function() {
// stuff here
var location = $(this).val(); // this = element on which the event was triggered
});
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.
I'm following Railscast 88 to create a dynamic dependent dropdown menu. http://railscasts.com/episodes/88-dynamic-select-menus
I'm rendering these dropdowns inside a partial that I'm using in a multi-model form. The form I'm using follows the Advanced Rails Recipes process by Ryan Bates. Because I'm rendering the dropdown inside a partial, I had to depart from strictly following the Railscast code. On the Railscast link provided above, comments 30-31 and 60-62 address these issues and provide an approach that I used.
For new records, everything is working great. I select a parent object from the dropdown, and the Javascript dynamically limits the child options to only those items that are associated with the parent I selected. I'm able to save my selections and everything works great.
The problem is that when I go back to the edit page, and I click on the child selection dropdown, the constraints tying it to the parent object are no longer in place. I'm now able to select any child, whether or not it's connected to the parent. This is a major user experience issue because the list of child objects is just too long and complicated. I need the child options to always depend on the parent that is selected.
Here's my code:
Controller#javascripts
def dynamic_varieties
#varieties = Variety.find(:all)
respond_to do |format|
format.js
end
end
Views#javascripts #dynamic_varieties.js.erb
var varieties = new Array();
<% for variety in #varieties -%>
varieties.push(new Array(<%= variety.product_id %>, '<%=h variety.name %>', <%= variety.id %>));
<% end -%>
function collectionSelected(e) {
product_id = e.getValue();
options = e.next(1).options;
options.length = 1;
varieties.each(function(variety) {
if (variety[0] == product_id) {
options[options.length] = new Option(variety[1], variety[2]);
}
});
}
Views#users #edit.html.erb
<% javascript 'dynamic_varieties' %>
<%= render :partial => 'form' %>
View#users #_form.html.erb
<%= add_season_link "+ Add another product" %>
<%= render :partial => 'season', :collection => #user.seasons %>
view#users #_season.html.erb
<div class="season">
<% new_or_existing = season.new_record? ? 'new' : 'existing' %>
<% prefix = "user[#{new_or_existing}_season_attributes][]" %>
<% fields_for prefix, season do |season_form| -%>
<%= error_messages_for :season, :object => season %>
<div class="each">
<p class="drop">
<label for = "user_product_id">Product:</label> <%= season_form.collection_select :product_id, Product.find(:all), :id, :name, {:prompt => "Select Product"}, {:onchange => "collectionSelected(this);"} %>
<label for="user_variety_id">Variety:</label>
<%= season_form.collection_select :variety_id, Variety.find(:all), :id, :name, :prompt => "Select Variety" %>
</p>
<p class="removeMarket">
<%= link_to_function "- Remove Product", "if(confirm('Are you sure you want to delete this product?')) $(this).up('.season').remove()" %>
</p>
</div>
<% end -%>
Here's your culprit:
<%= season_form.collection_select :variety_id, Variety.find(:all),
:id, :name, :prompt => "Select Variety" %>
Works perfectly on a new record because it's showing everything, and gets overwritten when the select changes on the other select box.
You need to do something like this:
<% varieties = season.product ? season.product.varieties : Variety.all %>
<%= season_form.select :variety_id,
options_from_collection_for_select(varieties, :id,
:name, season.variety_id), :prompt => "Select Variety" %>
Which will use only the Varieties linked to season.product. If season.product doesn't exist it lists all of them. It will also automatically select the right one if the existing record had a variety_id.
It also wouldn't hurt to change.
<%= season_form.collection_select :product_id, Product.find(:all),
:id, :name, {:prompt => "Select Product"},
{:onchange => "collectionSelected(this);"} %>
to
<%= season_form.select :product_id,
options_from_collection_for_select(Product.find(:all),
:id, :name, season.product), {:prompt => "Select Product"},
{:onchange => "collectionSelected(this);"} %>
Which will select the proper product on page load. This second part is essentially the Rails way of doing what BYK's first suggestion was. However, given the nature of the onchange method given to the select box, this line on its own would not solve the problem. It would just enhance the user experience by highlighting the product associated with the season.
I think you have two options:
Give one of the products(or simply the first element of the product list) a "selected" attribute which will force the browser to select that one always.
Trigger the "collectionSelected" function on "dom ready" or "window.onload" with giving the product list selectbox as its parameter.
And a note: never, ever trust JavaScript to force the user to send proper data to the server.