Ruby on Rails pass reference to yield (template) - ruby-on-rails

To make long story short: each of my tabs has its own form, so I decided to make a single layout and just to have a forms themselves as a variable content for a layout.
But I need to have form_for to be in a layout, rather then having it in each of the forms, because I have some other common form elements in the layout.
So how can I pass the form builder's reference f to the template ?
Layout code:
<% content_for(:content) do %>
<%= form_for current_form do |f| %>
<%= yield %>
<%= f.submit "Submit" %>
<% end %>
<% end %>
Is it possible ?
P.S Found this one: DRYing up a helper: wrap form_for and access local form variable (#rubish's answer), but <%= yield f %> doesn't seem to be working, f still remains undefined for the view.

Why don't you make a common template (not layout) for the tabs, and use a partial template for the content of each tab?
Then you can do something like:
<%= render :partial => #tab_name, :locals => {:form => f} %>

You can render a template in Rails that accepts a block by using the layout option of render
Let's say you have a form that you render a few times but you would like to customize your submit section each time. You can achieve this by rendering your form partial as layout and passing in a block. Your template or partial then serves as the surrounding layout of the block that you pass in. You can then yield back the form to the block and access the form in your block.
record/_form.haml
= form_for record do |form|
...
.form-actions
yield(form)
In order to make your template record/_form.haml accept a block when you render it, you can render your template as the layout for your block using the layout option of render:
record/edit.haml
= render layout: 'form', locals: { record: record, ... } do |form|
.form-actions--primary
= form.button :submit

Related

Why is this block not working on my ERB template

<%= render layout: "shared/some_template",
locals: {
variable_value: true
} do %>
<%= hidden_field_tag "ids[]", "ng-value": "sth.id" %>
<% end %>
The template on some template has some html and a yield call where the "hidden" field should go but it's not rendering it, it does render everything else inside the template, it just drops the hidden_field_tag part.
Any idea how can I solve this? it works on the HAML version when I tried it, but not on ERB, is that not available for ERB?
You are passing a &block to render, which is doing nothing with it.
To pass a callback to a partial, build a lambda and put it in a local variable:
<%=
lamb = lambda{ hidden_field_tag 'ids[]', 'ng-value' => 'sth.id' }
locals = { variable_value: true, callback: lamb }
render layout: 'shared/some_template', locals: locals
%>
Now inside the template call <%= callback.call %>.
(Note, BTW, that I used ' instead of ", because we are not using the special features of ". And note I introduced a local variable, locals, instead of creatively indenting the render call.)

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.

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.

Pass Parameters in render - Rails 3

I've seen a couple questions on this but haven't been able to solve it...
I'm trying to pass a parameter while rendering a partial (similar to domainname.com/memory_books/new?fbookupload=yes)
Right now, I use this line:
<%= render :partial => '/memory_books/new', :fbookupload => "yes" %>
and in the partial, I have tried to get the content of fbookupload by using:
<%= fbookupload %>
which gives an "undefined local variable" error and
<%= params.inspect %>
which does not show fbookupload as a parameter.
How can I have the partial pass along the parameter :fbookupload?
Thank you.
UPDATE:
Could it have anything to do with the fact that I'm rendering this within a render?
i.e. the page (/fbookphotos/show) that has
<%= render :partial => '/memory_books/new', :fbookupload => "yes" %>
is being rendered by another page with (posts/show) via:
<%= render :partial => '/fbookphotos/show' %>
so I'm rendering this within a render.
try this:
<%= render :partial => '/memory_books/new', :locals => {:fbookupload => "yes"} %>
Taking it out of the comments for posterity. This syntax is correct:
render '/memory_books/new', fbookupload: "yes"
But if there is a reference to rendering the same partial without specifying the local variables, e.g.
render '/memory_books/new'
then fbookupload variable becomes unavailable. The same applies to multiple local variables, e.g.
render 'my_partial', var1: 'qq', var2: 'qqq'
will work if only occurs once. But if there is something like that somewhere else in the code
render 'my_partial', var1: 'qq'
then the var2 will become unavailable. Go figure ...
To do it your way:
In the main view:
<% fbookupload = "yes" %>
<%= render :partial => '/memory_books/new', :locals => {:fbookupload => fbookupload} %>
And in the partial:
<%= fbookupload %>
2nd option:
Ideally in the controller, otherwise in the view, define an instance variable: #fbookupload = "yes". Then it is available everywhere. The partial will then be : <%= #fbookupload %>
Params is just request parameter, so if u want to pass it in params u have to add it to your url ?fbookupload=yes or assign it params[:fbookupload] = "yes", but i don't think that is a good idea.
But if u need to use params[:fbookupload]', u can replace it withparams[:fbookupload] || fbookupload', and pass fbookupload in locals hash for partial.
render can be called with or without the partial param, and there seems to be some confusion around the differences between these two forms.
The following two are equivalent:
<%= render "my_partial', my_param: true %>
and:
<%= render partial: "my_partial', locals: { my_param: true } %>
The first is a shorthand that allows you to omit partial:. With this shorthand, local variables are also not nested under locals:. This is explained well in the documentation (see 'Rendering the default case').
In the two cases above, you would access my_param in the partial directly with my_param.
One other source of confusion is that if you render the partial somewhere without passing my_param, then the partial will fail when it tries to access it. To get around this, you can access the local with local_assigns[:my_param] instead of my_param, which will give you nil if the param is not defined instead of erroring, as described in this documentation. Another alternative is to use defined?(my_param) before accessing it.

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