Populating jQuery Field Dynamically Depending on Rails Field - ruby-on-rails

In my Rails (3.1) app, I have a shopping cart where I show the sales tax field and total on a page after the person enters their State.
It works correctly, except when there is an error and the person has already entered their State. In that case, the field value doesn't change (since the State field is populated already), so the jQuery (update_sales_tax) isn't triggered again.
Is there a way to render the view again (in case of errors) so the person doesn't need to fill out the entire form again, but have the sales tax calculated still? Or maybe a better approach to showing the user their sales tax on the page dynamically (since it's dependent on the State)?
In other words, my controller does this format.html {render :action => 'new'} if there is an error in Orders#new. It populates the fields in the form that the person already entered, however it doesn't recalculate the sales tax which is triggered onChange of the :state field via jQuery. How do I get this to recalculate if the view renders again and :state is already populated?
In the Orders#new view I have:
<%= form_for(#order, :method => :post) do |f| %>
<%= f.hidden_field :total_sales_tax, :value => #tax_amount %>
<%= f.fields_for :products_shipping do |shipping | %>
<%= shipping.select(:state , Order::STATES ,{:prompt => 'Select State'}, :id => "ship_state",:onchange => "update_sales_tax()") %>
<% end %>
<% end %>
<script>
function update_sales_tax(){
jQuery.ajax({
url: '/orders/order_update',
dataType: 'script',
type:'get',
data:'stateTo='+jQuery("#ship_state").val()
})
}
</script>
In orders/order_update.js.erb:
jQuery('#cart_details').html('<%= escape_javascript(render(:partial => #cart)) %>');
jQuery('#order_amount').val('<%= format("%.2f", #grand_total) %>');
jQuery('#order_total_shipping').val('<%= format("%.2f", #cart.total_shipping) %>');
jQuery('#order_total_sales_tax').val('<%= format("%.2f", #tax_amount) %>');

I figured it out, I just needed to add this line above the function:
window.onload = update_sales_tax();

Related

undefined variable 'f' when rendering portions of a form through ajax

I have a situation where a Contact has many Leads and a Lead belongs to Profile polymorphically. I successfully rendered the form as such:
# _form.html.erb
<%= form_for #lead do |f| %>
<%= f.fields_for :contact do |builder| %>
<%= render "contact_fields", :f => builder %>
<%= render "leads_field", f: f %>
<% end %>
<% end %>
# lead_field.html.erb
<%= f.select :practice_type, PRACTICE_TYPES.collect {|type| [ type, type ] }
<%= f.fields_for :practice do |builder| %>
<%= render "#{#practice.class.name.underscore}_field", :f => builder %>
<% end %>
The above works fine with page load. However, a user can select a practice from dropdown menu and then I send ajax request to repopulate the form building an association with that practice:
$('#lead_practice_type').change(function() {
$.ajax({
url: "/leads/new",
data: {
profile_type : $(this).val()
},
dataType: "script"
});
});
The error occurs in new.js.erb:
$("#form_holder").html("<%= escape_javascript(render(:partial => "leads_field"))%>");
I want to only render that partial (if I render whole form, then all their contact information would be erased.). But because that partial contains a local f variable, it blows up:
NameError - undefined local variable or method `f'
I want to maintain the relations between the objects but also update just that one partial. But it appears I cannot do it through the new.js.erb script because when it renders the partial, there is no local variable 'f' passed.
Is there another alternative to achieve what I want?
I found this problem difficult to solve on two fronts. First, There is no way you can identify the form builder from the controller action and javascript. Second, although I am quite familiar with the railscasts episode http://railscasts.com/episodes/196-nested-model-form-revised?view=comments, in that episode he knew the fields_for to generate at compile time, and therefore he hid the markup in the data field of a link. Unfortunately, in my case I would not know the fields_for the user wants until runtime (when the browser loads and user selects an option).
I also realized that fields_for is important to maintain nice associations for form submission so that the create action would be simply: save the parent resource and it saves its associations.
The best solution I could come up with is as follows.
1) Create ALL the polymorphic associations with fields_for and hide the ones the user doesn't want via a select form field:
# template
<div id="runtime-profile">
<% Lead::TYPES.each_with_index do |association, index| %>
<div id="profile_<%= association %>">
<fieldset class="field-border">
<legend class="field-border"><%= association.underscore %></legend>
<%= f.fields_for :practice, build_builder_association(f, association) do |builder| %>
<%= render "#{builder.object.class.name.underscore}_field", :f => builder %>
<% end %>
</div>
<% end %>
</div>
# helper
def build_builder_association(f, association)
f.object.practice_type = association
f.object.build_practice({})
end
Use Javascript to hide and show the templates based on an option selected from dropdown, and then when form is submitted, remove the hidden elements from the DOM:
$("#new_lead").submit(function(){
$("[id^='profile_']:hidden").remove();
})
And that creates the original desired behavior I wanted. However, the solution is far from clean and any better suggestions are welcome.

Ajax form within a form with no layout

So I'm new to rails and having a little bit of trouble my situation is that I have a products model that has some Images attached to it. I would like on my products page to have a button to create a new image via ajax.
Inside my products _form view i have:
<%= simple_form_for(#product) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :name %>
<%= f.input :description, :input_html => {:class => "wysihtml5 span6", :style => "height:400px;"} %>
<%= f.association :images, label_method: :name, value_method: :id %>
<h4>Upload new file</h4>
</div>
<%= link_to 'Add Image', '/images/new', :remote => true, :"data-replace" => "#image-form" %>
<div id="image-form">
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
And it does successfully Load the page via ajax.
But I would like to load it without the layout. Is this possible?
Do I need to create a new action that renders the partial form and has no layout??
So I'm assuming you have it requesting and returning html and not js? There are a couple of thoughts I have on this:
If you only ever need for this request to return from an AJAX request, then you could simply tell the controller to always render layout: false
If you would like to allow the controller to return a full page on occasion, you can either accept an argument in the get request and change the output accordingly, or you can tell the controller to:
respond_to do |format|
format.html # will render default with no block passed
format.js { render layout: false }
end
This should just work with the code in your form right now, because the :remote => true tells the controller to return js if possible, but accessing images/new from your browser will request html.
(Requesting js does not mean that you actually have to return js, as I don't in this case; it's up to you to take the proper action with what is returned. Some might consider it bad form, though, to return something other than what is technically requested.)
You can use the following in your action. Let your action is new.
def new
#your code goes here
render :layout => false
end

Rails ajax form not submitting from within another

I have a Rails 3.2 ajax form that creates a new Room for a Hotel. The new Room ajax form works correctly on the Room index page, but once I embed the new Room ajax form on a Hotel edit page, the form is submitted normally without using Ajax.
To create the new Room form, I use the following link on the Hotel edit.html.erb page:
<%#= link_to 'Add a Room', new_room_path(:hotel_id => #hotel.id), :remote => true, :class => 'new_room' %>
This loads the following form partial on to that same page:
<%= form_for #room, :remote => true, :html => { :class => "remote-form" } do |f| %>
<%= f.text_field :number %>
<%= f.text_field :size %>
<% if(params.has_key?(:'hotel_id')) %>
<% #hotel_id = params[:hotel_id] %>
<%= f.hidden_field :hotel_id, :value => #hotel_id %>
<% else %>
<%= f.collection_select(:hotel_id, Hotel.all, :id, :name) %>
<% end %>
<%= f.submit "Add this room", :class => 'room_create' %>
<%= link_to 'Cancel', '#', :class => "room_cancel" %>
<% end %>
And finally, I have the following in my create.js.erb (inside the rooms folder):
alert('Test creating a room');
var content = $('<%= escape_javascript(render(#room)) %>');
$("#room_list tbody").append(content);
The create.js.erb is not executed and the form is submitted regularly (non-ajax) and I finally arrive on the room show.html.erb page.
Why is the form be working correctly on the Units index page, but not on the associated Hotel edit page?
Even when you set :remote => true, Rails generates a form tag. Nested form tags are not supported by browsers and will result in unpredictable behavior.
You should rethink the views architecture here. Probably you can have the forms for the rooms outside of the form for the hotel, or maybe you can use fields_for and accepts_nested_attributes_for to edit children objects.
Here's a full example on how to use nested attributes: Nested Attributes Examples.
You cannot nest a form inside a form in HTML. When you click any submit button on a form, even if it's inside another form, only the outermost form will be properly submitted.
You can either use nested attributes to add the attributes for the room directly to the form, so that when the overall form is submitted so are all the rooms... or use a div and a link, instead of a form and a submit button.

Ruby-on-Rails: populate select box based off prior select

I have a set of 3 collection_selects that I would like to filter the next select. Upon selecting an item in the first select box, it should limit the possible values of the second and third select boxes.
A similar data model would be as follows...
Tables, and fields
Lock
lock_id
brand_id
master_id
regular_id
Lock Brand
brand_id
Master Key
master_id
brand_id
Regular Key
regular_id
brand_id
So while filling in the form for adding a new Lock there would be select boxes for brand, master, and regular. Upon selecting a brand, it would then limit the master and regular keys to those that are only of the selected brand.
I've found a couple partial examples of how to do this, however none are complete. I'd appreciate a complete example of how to implement this.
All you need to do is bind the selects with an event listener that fires off an ajax request populating the other 2 partials containing the subsequent selects. i.e.
<%= form_for #thing, :remote => true do, :update => 'second_form' |f| %>
<%= hidden_field_tag 'form_partial', :value => 'form_2' %>
<%= select_tag :some_param, [[a,a],[b,b],[c,c]] :class => 'submittable' %>
<%= f.label :checked %>
<%= thing.name %>
<% end %>
<div id='second_form'>
</div>
<div id='third_form'>
</div>
class ThingController < ApplicationController
def update
checkboxes = some_logic_to_select_checkboxes from params[:some_params]
render :partial => params[:form_partial], :locals => {:select_menu => select_menu}
end
end
$('.submittable').live('change', function() {
$(this).parents('form:first').submit();
return false;
});
Your form partial should contain your forms populated with whatever selects you want enabled. Thought is was redundant to just repeat the form.

Multiple forms from single partial

I have a single form partial:
<% form_remote_for(#snapshot_comment, :html => { :class => "snapshot_comment", :id => "snapshot_comment" }, :auth_key => params[:auth_key]) do |f| %>
some stuff...
<span class="button"><%= f.submit 'Submit ยป' %></span>
or cancel
<% end %>
It gets input into tooltips into a page multiple times via jQuery (targeting snapshot_id). This works well:
//Insert class and partial by targeting snapshot id
$j('<div class="info"><%= escape_javascript(render :partial => "snapshot_comments",
:locals => {
:snapshot => #snapshot,
:comments => #comments
})
%></div>').appendTo("#slide_<%= #snapshot.id %>");
The partial (snapshot_comments) contains the code for the form:
<%= render_snapshot_comment_form_for snapshot %>
The problem is that when data gets posted - it posts as many times as there are forms on the page.
So If a user enters info and clicks submit --- the content is inserted in every tooltip rather than just the tooltip it belongs to. I am using Qtip2.
Is there a way to submit the form specific to the tooltip it resides in?
If the form is being rendered multiple times per page from the same partial, then it sounds like your element IDs are the same from form to form within that page.
If your elements don't have unique IDs, then the response gets put into each place where that ID appears.
For example - if you have a page that puts results into a div with an id of "output", and that div gets rendered from a partial 5 times (each time with the same id of "output"), then you're going to see the response rendered 5 times.
Do a "view source" on your resulting page, and look for duplicate IDs, and see if that's the case.

Resources