undefined variable 'f' when rendering portions of a form through ajax - ruby-on-rails

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.

Related

Passing form to partial with remote true update - Rails

I'm using Rails 5.2.2. I have a form with remote: true. I have a partial that displays shipping methods where I render nested shipping methods available for a quote. My form accepts nested attributes also.
My form works fine, but on update I am wanting to render the updated shipping methods if they have changed but since I pass the original form to my partial, I don't have that variable available on update. Here is my partial
_shipping_rates.html.erb
<h2> Shipping Rates</h2>
<div id="quote-shipping-rates">
<%= form.collection_radio_buttons(:shipping_method_id, quote.shipping_methods.sort_by(&:price), :id, :name, { checked: quote.shipping_method_id }) do |b| %>
<%= b.radio_button %>
<%= b.label %>
(<span class="text-red"><%= number_to_currency(b.object.price) %></span>)
<br />
<% end %>
On the initial page load I call the partial like this:
<%= render 'shipping_methods', :form => u , :quote => #quote %>
On update, I have the following file that executes JavaScript to update the DOM.
update.js.erb
I want to do something like this but I wasn't sure if there was some Rails way to recreate the form variable and pass it to the partial.
$("#quote-shipping-rates").html("");
$("<%= escape_javascript(render :partial => "shipping_methods", :form => u , :quote => #quote) %>").appendTo("#quote-shipping-rates");

Rails - Multiple forms, different Models (Objects), one submit button

I have a view with 3 forms, Schedules, Workouts and Exercises, all behaving like an edit form, each. And one submit(save) button in the all the view.
When I click on the save button. Every data changed on those forms should be updated after click.
What is the best solution for this ? Javascript updating each data separated ? How to do that ? Is there a more Rails way to do this easily ?
My difficulty is how to integrated all those models in one view, while all this is happening in the show(view) from the Student model.
If you're implementing something like a profile / edit page (where you can save all the records at once), the two ways I would look at would either be to save the forms via Ajax, or use a single submit method to handle them
Ajax
The ajax method would be the most conventional:
Every form you submit will go to the form's own update method in the backend
Each form could be handled by a single button, but it's best to split them up
#app/controllers/profile_controller.rb
def edit
#schedules = Schedule.all #-> not sure how many records you're using
#workouts = Workout.all
#exercises = Exercise.all
end
#app/views/profile/edit.html.erb
<%= form_for #schedule do |f| %>
<%= f.text_field :test %>
<% end %>
# -> other forms
<%= button_to "Save", "#", id: "save" %>
#app/assets/javascripts/application.js
$("#save").on("click", function() {
$("form").submit(); // we'll have to define the form to submit
});
Single
If you submit all the forms as one, you'll have to encase them all in a single form, as sending different errors. This could be achieved by using _, and handled in the backend by looping through the different params, saving each one individually.
I'd do this:
#app/controllers/application_controller.rb
def submit
types = %w(schedules exercises workouts)
for type in types do
type.constantize.update_attributes()
end
end
This allows you to create a form with the different data types submitted in the same action:
#app/views/profile/edit.html.erb
<%= form_tag profile_submit_path do %>
<%= fields_for #schedules do |f| %>
<%= f.text_field :title %>
<% end %>
# -> fields_for for the other objects
<% end %>
This will allow you to send the updated objects to your controller, allowing them to submit
If all of your models (Schedules, Workouts and Exercises) are associated, using fields_for should be a good option.
From the above link:
<%= form_for #person do |person_form| %>
First name: <%= person_form.text_field :first_name %>
Last name : <%= person_form.text_field :last_name %>
<%= fields_for :permission, #person.permission do |permission_fields| %>
Admin? : <%= permission_fields.check_box :admin %>
<% end %>
<%= f.submit %>
<% end %>
Read the guides.
You could have some simple javascript that iterates over all form tags and submits each of them.
Alternatively, if you are going to use javascript anyways, you could follow an AJAXish auto-save approach upon changing any field.
But I think it might be cleaner if you just had one form for multiple models, using fields_for.

How to dynamically add a (HABTM) collection_select + associated text_field in a Rails form?

For a personal invoicing app in rails I could use some advice on the following: In a new invoice form, how to dynamically add a new row consisting of a collection_select for products and an associated text_field for amount by clicking add new product?
Since one invoice has many products and a product has many invoices, I figured this is a HABTM relationship both ways. Therefore I have created the tables invoices, products, and their join table invoices_products.
I would like the invoices/new page to have a form where a user can use a collection_select to pick a product (all products are already preloaded in the products table) and fill in the amount in a text field behind it.
A user should be able to duplicate these two fields (collection_select and text_field) by clicking add new product, something like RailsCast #403 Dynamic Forms 04:50.
Now how would I go about doing that? And in what table would I put the data from the amount text_field, since there are multiple products per invoice so multiple amounts?
Any answer in the right direction will be greatly appreciated!
This depends on how many additional fields you want
A good tutorial for this can be found here
Ajax
The right way would be to add the element dynamically with Ajax
This is tricky because it means you're not going to have the form_builder object available for the dynamically added items. Regardless, you can still do it with this code:
#config/routes.rb
get "new_field_path", to: "invoices#new_item"
#app/views/controller/index.html.erb
<%= ... form %>
<%= link_to "Add Field", new_field_path, remote: :true %>
Controller
#app/controllers/invoices_controller.rb
def new_item
#invoice = Invoice.new
#invoice.products.build
render "new_item", layout: false
end
#app/views/invoices/new_item.html.erb
<%= form_for #invoice do |f| %>
<%= render partial: "products_fields", locals: { f: f } %>
<% end %>
#app/views/invoices/_products_fields.html.erb
<%= f.fields_for :products, child_index: Time.now.to_i do |p| %>
<%= p.text_field :name %>
<% end %>
Form
#app/views/invoices/new.html.erb
<%= form_for #invoice do |f| %>
<%= render partial: "products_fields", locals: { f: f } %>
<%= link_to "Add Field", new_field_path, remote: :true %>
<%= f.submit %>
<% end %>

How to disable all form_for input fields in Ruby on Rails app?

I am trying to DRY up my Rails application a bit, so I would like to render a form in my show view but disable all input fields.
// show.html.erb
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f %>
<% end %>
What would be the best way to do that?
Thanks for any help.
Javascript
One way would be to do it using JS. Include a div with a specific class in the show view :
// show.html.erb
<div class='disable_input'>
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f %>
<% end %>
</div>
Then in your JS file :
$('.disable_input :input').prop('disabled', true);
Rails
If you want to actually generate it server side, you can pass a variable to your partial that will tell the partial if it has to add the disabled option on each field. It's a bit more work though!
Using a variable, you could do something like this :
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f, :disabled => true %>
<% end %>
In the partial :
<% disabled ||= false
#We do this so if disabled is not passed to the partial it doesn't crash.
# We default it to false
%>
<% # Then for all your fields, add disabled: disabled %>
<%= f.text_field :some_attribute, disabled: disabled %>
Form builder
Edit : actually, one way to avoid explicitly passing disabled everywhere would be to create a Custom form builder. There's some good resources talking about it, like this one : http://johnford.is/writing-a-custom-formbuilder-in-rails/
In this example, it's done for onkeypress, shouldn't be hard to adapt for your case!
You can wrap all fields in <fieldset disabled>
// show.html.erb
<%= form_for(#project) do |f| %>
<fieldset disabled>
<%= render 'fields', :f => f %>
</fieldset>
<% end %>
You can use stylesheets for this thing.
The show action might be in controller say 'Project', hence you might be having a file in stylesheets with the name of your controller.
Now enclose your form in show.html.erb in a div, and give it a unique id ,say 'disable_input', that you wont be giving to any element in any page.
Now disable all input fields in you css under this div. You can write it like this..
disable_input input{
# whatever you want to do
}
Hence no need to code.

Rails 3 + Ajax: how to access my local form builder instance

I have a form that displays a set of inputs. I also have a button, and when clicked, I make an ajax request which is supposed to replace the existing inputs with a different set of inputs.
All my ajaxy linking stuff up works fine. The problem is that I'm using a form_for, so in order to display the new form inputs, I need the form builder instance.
View File
<%= simple_form_for #order do |f| %>
<div id="info">
<%= render 'my_first_form_fields_partial', f: f %>
</div>
<%= link_to 'Change inputs', change_inputs_path, remote: true %>
<% end %>
I'd like my js.erb to look like this, but f is only defined within the scope of the form in the view.
$('#info').html("<%= escape_javascript(render 'my_second_fields_partial', f: f) %>");
How can I work it so that I can get f into that partial somehow?
Can you use fields_for in your partial, and pass the #object to it? That way you don't need to pass a form builder?
partial:
<%= fields_for object do |f| %>
f.text_field :field_name
<% end %>
$('#info').html("<%= escape_javascript(render 'my_second_fields_partial', object: #object) %>
Many thanks to #flyfish, his answer helped me solved how to do ajax with nested attributes. I took #flynfish answer and tweeked it for my situation:
<%= fields_for object do |f| %>
<%= f.fields nested_object, child_index: Time.now.to_i do |builder| %>
<% end %>
$('#info').html("<%= escape_javascript(render 'my_second_fields_partial', object: #object), nested_object: #nested_object %>
The child_index is important for without it your params hash will build with [0] and will then overlay any other records built with the initial fields_for which starts at zero and increments from there.

Resources