How to make dynamic dropdown in Rails? - javascript

I'm working on a e-commerce project where there will be radio buttons for size selection. I have a dropdown for quantity. I want to make this dropdown dynamic based on stock available for the user selected size. Can anyone tell me how this can be done on Rails? without cluttering my view file with lot of javascript!?

If you are using Rails 3 with prototype (default afaik), you could use the prototype legacy helpers (Link on Github) to add an observer and render a partial view for your dropdown box. You could add an observer like this
<%= observe_field 'element_var_name',
:url => { :action => "another_action_here" },
:update => "div_tag_to_update",
:with => "'selected='+ escape($('element_var_name').value)" %>
Be sure to adjust element_var_name and the action to your situation. div_tag_to_update is the div that will update with the dropdown box. The another_action_here action should render a view like this:
def call_ids_by_type
#element_list = ... # whatever fits for you, like: [[key, value],[key, value]]
render :layout => false
end
In the partial view, you can use the element list to generate the dropdown box:
<%= f.select :var_name, #element_list %>

Related

Rails5: how to pass element to bs modal with form_tag

I have an index view listing my elements. I want a button link to open a modal view containing a form_tag to pass some text which then get's sent to a third party API, but 'linked' to the element (it's ID) passed.
So far I get the modal showing and it's submitting correctly. But: the modal get's always the first/last element of the index list.
/config/routes.rb
namespace: manage do
resources :elements do
member do
post :my_action
end
end
end
/manage/elements/index.html.haml
#elements.each do |element|
%tr
%td
= link_to 'btn-text', '#myModal', data: { toggle: 'modal', target: '#myModal'}
= render 'manage/elements/element_modal', element: element
/manage/elements/_element_modal.html.haml
#myModal.modal.fade{...}
.modal-dialog
.modal-content
= form_tag path_to_my_action(element), method: :post do
= text_field_tag :text, params[:text]
= submit_tag
I tried different approaches (a modal for each element, data-attributes, AJAX) like found eg here: Bootstrap Modal in Rails Keeps displaying the first record
But actually I can't get any of these working and I don't get why. Can you give me a hint based on above example so I finally get this working? Thanks!

Multiselect Rails 3, Default Selections with Javascript

I have a Rails 3 application where I allow the user to apply tags to their listing. I've read a few stackoverflow threads with similar questions but this one is unique since I am using a frontend framework. I am using the webarch framework: http://www.revox.io/webarch/form_elements.html
If you CTRL + F "Multi Select" you can see the exact element that I am using.
Summary
I use this code to populate tags:
<%= f.collection_select :tag_ids, Tag.all, :id, :name, {:selected => #listing.tags.map(&:id).map(&:to_i)}, { :multiple => true, :style => "width:100%", :id => "multi", :class => "select2-offscreen", :placeholder => "Select Tags." } %>
This works to assign tags, but the tags that have already been saved to a listing do not appear in the dropdown on the edit view. For example, if the a user has tagged their listing as "Jim" and "Lucy", they should see this when they arrive on the edit view.
Framework's JS code
The framework is added to the site. In the JS files, it has the following code:
$(document).ready(function(){
//Dropdown menu - select2 plug-in
$("#source").select2();
//Multiselect - Select2 plug-in
$("#multi").val(["Jim","Lucy"]).select2();
This is excellent starting logic, but obviously I don't want "Jim" "Lucy" manually coded into my dropdowns. How can I override this in js to make it work correctly for the existing tags?
{:selected => #listing.tags.map(&:id).map(&:to_i)}
I just had to go into the plugin's files and change the code to:
$(document).ready(function(){
//Dropdown menu - select2 plug-in
$("#source").select2();
//Multiselect - Select2 plug-in
var multi = $("#multi");
var selected = multi.find("option[selected]").map(function() { return this.value; });
multi.val(selected).select2();

How to make a change event only run one time the event is called for a class

I'm designing a multi dynamic select menus, i.e., I have a menu for brands, after the user select the brand, using JavaScript and AJAX, I will search for the models available from that brand and add them to the second select menu. This process repeats again but this time showing the features of the model selected.
To do this, and because I have many different areas that need the same system, I use a class with the same name in every brand select menu and another one to every model select menu.
<div class='brand_select' id='14'>
<%= f.collection_select :brand, Product.find_all_by_area(14, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(14), :model, :model, :prompt => 'Choose model' %>
</div>
<div class='brand_select' id='15'>
<%= f.collection_select :brand, Product.find_all_by_area(15, :group => 'brand'), :brand, :brand, :prompt => 'Choose brand' %>
</div>
<div class='model_select'>
<%= f.collection_select :model, Product.find_all_by_area(15), :model, :model, :prompt => 'Choose model' %>
</div>
And the JavaScript:
$('.brand_select').change(function(event) {
// option selected
var brand=$(event.target).find('option:selected').val();
// if none is selected
if (brand == ''){
$(event.target).parent().parent().find('.modelo_select').hide();
$(event.target).parent().parent().find('.caracteristica').hide();
}
else {
$(event.target).parent().parent().find('.modelo_select').show();
// find id to search on the database
var id=$(event.target).parent().attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/model/"+brand+"/"+id+"/"+target,
brand: brand,
id: id,
target: target
});
}
});
$('.model_select').change(function(event) {
// find model selected to search on the database
var model=$(event.target).find('option:selected').val();
// find brand selected to search on the database
var brand=$(event.target).parent().parent().find('.marca_select').find('option:selected').val();
// find id to search on the database
var id=$(event.target).parent().parent().find('.marca_select').attr('id');
// find the target (id of the object)
var target=$(event.target).attr('id');
$.ajax({
type: "POST",
url: "http://"+location.host+"/feature/"+brand+"/"+model+"/"+id+"/"+target,
brand: brand,
model: model,
id: id,
target: target
});
});
This code works but it repeats the event change the same number of times as the classes with that name.
What I want to do is for the function to run only one time every time a change event is called for the class.
I don't know if this is possible with class structure that I have or if I have to associate an id or a class with different names for each area to the function.
I don't see why the event should fire twice because all you're doing with $(selector).change is saying that every time a change event fires on something with that selector you want to handle it. I even ran a quick test to be sure and it doesn't fire more than once.
Can you explain a bit better what the symptom actually is? As in, what actually happens twice? Does everything in your event handler happens twice?
I was thinking that your selectors for the actions you perform on the parents might be a bit too lax ($(event.target).parent().parent()) so if you only want to do something on the container where your event was fired that wouldn't be the best way (but then again I don't know what your end purpose is here).
That for your help, I found out that the problem has nothing to do with Javascript but is instead on the Ruby on Rails.
I was adding on application.html.erb other js files and if you have the //= require_tree on the application.js it adds every js file in the tree, so adding js files on application.html.erb will make them repeat and cause strange behaviors like this one.

Click rails button to permanently change data model and css

So I'm creating a photo proofing web app for a client. I want him to be able to skim the images on the site and under each image is an 'Approve' button, which he can click and the image border will turn green, signaling it's good for export.
Now my JS/Jquery knowledge is pretty limited, but I know that this might go deeper than just front-end work, because I want to see those changes even after browser is closed, which I think requires a both back-end and front-end solution.
I'm thinking to create a boolean attribute under my image model, and when you click the 'Approve' button, it'll switch the boolean value to true, which will change the css class to turn green. Is there a way rails can detect a boolean value and change the css accordingly? I want to be able to see the changes my client made.
Would appreciate any feedback/advice on my approach, or if there's a better way out there to go about this. Thanks!
first add on your images table an approved column with type boolean
and on your images controller and these action
def approve
#image =Image.find(params[:id])
#image.update_column(:approved,true)
respond_to do |format|
format.js
end
end
in routes add these method
resources :images do
member do
put :approve
end
end
in your html
<div class="image">
<img src="<%=#image.source%>" />
<%= link_to "approve",approve_image_path(#image),:remote=>true,:method=>"PUT",:class=>"approve_me"%>
</div>
in your image.js file add these method
$("body").on("click",".approve_me",function(e){
e.preventDefault();
$(this).parents(".image").find("img).css("border-color","green");
});
I think you are almost there.
Firstly - yes, changing the css with javascript on runtime will immediately change the appearance of dom elements.
On the client side - you want to indicate to the user which images have been selected, but you also need to store the approved value for each element in your submit form.
In Rails it is common to create hidden input elements storing additional data. Given the task at hand - they can store a value of 0/1 - rejected/approved. You can come up with your own convention for naming the image/select values.
Then, in your view, add onclick listeners to your images pointing to a javascript function:
1) checking if the clicked element is already selected,
2) changing the css of the current element,
3) updating the hidden input value for that element.
Here is a dummy demonstration - jsfiddle
Later, you can then get the approved/rejected value from the params in your controller, like (see below).
In case you want to assign a css class when constructing the view and depending on an approved flag value, you could do something like:
<img id="image_<%= image_id %>" src="<%= image_item.path" class="<%= (image_item.approved.to_i == 1) ? 'approved_img' : 'non_appr_img' %>" %>
<input id="image_<%= image_id %>_app_flg" type="hidden" value="<%= image_item.approved %>" />
where image_item is an object properties path and approved (self explanatory), image_id the id of the image object, approved_img and non_appr_img - css classes.
I'm not discussing the back-end for storing the approved flag, as it seems it is out of the scope of the question
EDIT
Brief concerning back-end
Given the you have an images model, extend it to include an approval property (prepare a database migration and edit your model .rb file to include the new columns).
In the view, include all of the hidden inputs inside a form which will be submitted to your controller(for example looping through an array on your images). For example:
<%= form_for :images, :url => {:action => "approve_images"}, :html => {:name => "testForm"} do |f| %>
<!-- TODO do stuff here - eg display images -->
<% #images.each do |imageItem| %>
<%= f.hidden_field "#{imageItem.id}_appproved", {:value => imageItem.approved}%>
<% end %>
<!-- TODO add a submit button -->
<% end %>
*You need to
Here :images is the controller, approve_images is the function in the controller that the form will be submitted to (include in routes), #images is an array with images data (from your model) and that you have prepared in the controller before rendering the view. I assume the images have ids and approved property.
This will yield in your view dom elements like this:
<input id="images_IMAGEID_appproved" name="images[IMAGEID_approved]" type="hidden" value="1" />
After submitting the form, in your controller you will be able to access these values like this:
img_approved = params[:images][IMAGEID+"_approved"]
Finally you can store that value to your database. Good luck! :)
I am omitting a lot of even more basic things, but I think the question is too broad as it is and there are plenty of resource detailing how to create,read,write a model, prepare data for the view etc. If not - please get started with http://guides.rubyonrails.org/getting_started.html
Thanks everyone who helped me answer this question below! I came up with a solution I'm pretty happy about, figured I'd share it hoping it'll help somebody else along the way.
The problem: I was looking for an AJAX solution that could make permanent changes to the data model. I wanted somebody to be able to toggle/highlight certain items on a page, and have those changes saved on the backend so I can view it later.
This required both a front-end ajax solution for the user interface and back-end solution so the ultimate changes will be saved in the data model so when I load up the site later, I can see the changes he made.
My solution (with the help of those who answered below):
Backend - I created a link that when pressed, would toggle the attribute in my model either true/false
Frontend - In order to give the client a real-time feel, I had to set the link to perform ajax requests and change the css accordingly.
My controller:
def approve
#pipe = Pipe.find(params[:id])
respond_to do |format|
if #pipe.toggle!(:approved)
format.html { redirect_to root_url }
format.js
else
format.html { render :index }
end
end
end
My pipes table has an approved:boolean attribute
My approve.js.erb file (I wrapped each pipe in a div using div_for(#pipe):
<% if #pipe.approved? %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid green');
$('div#<%= dom_id(#pipe) %>').children('a').text('un-approve');
<% else %>
$('div#<%= dom_id(#pipe) %>').children('.flexslider').css('border','4px solid white');
$('div#<%= dom_id(#pipe) %>').children('a').text('approve');
<% end %>
My Application Helper:
def approve_text(approvable)
approvable.approved? ? 'un-approve' : 'approve'
end
My trigger link (which uses abovementioned helper):
<%= link_to approve_text(pipe), approve_pipe_path(pipe), remote: true, method: 'PUT', class: approve_text(pipe) %>
My routes:
resources :pipes do
member do
put :approve
end
end
Again, thanks for those who helped provide answers. This is a solution I'm pretty happy with. I know it probably could use some help being refactored. If anyone has suggestions, would love to hear about it!

Correct object not passing to remote form

Each user of my site has their own profile page, which contains several items of the same class, call them job_items, which are instance variables of the job class. I'm working on adding edit functionality for each item. In each item I have
<%= link_to "edit", job_item, :onclick => "$('#edit_job').modal();", :remote => "true" %>
Which I want to bring up a modal containing the appropriate form:
...
<div class="modal-body">
<%= semantic_form_for(job_item, :html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
However, any time I click edit, it doesn't matter which job_item I clicked edit within, the modal appears with a form for the same job_item, namely the first on the page. How can I set which job_item I want to be edited in the form? I'm confused because if I hold my mouse over the edit link, the correct job url appears, however it is not being passed to the javascript modal.
Is it possible you have more than one element with the id "edit_job" in your page? If so, $('#edit_job') will just choose the first one, because it assumes the ids are unique. You could fix this by making sure the ids are unique
:onclick => "$('#edit_job_#{job_item.id}').modal();"
and changing the corresponding element ids accordingly.

Categories

Resources