I have a rails 4 app that has a form that looks like:
<%= form_for #store, :html => {:multipart => true} do |f| %>
<%= f.fields_for :products do |product| %>
<%= render partial: "edit_product_fields", locals: {product:product} %>
<% end %>
<%= f.submit %>
<% end %>
and the edit_product_fields partial looks like:
<select>
<option value="Textbook" <% if product.type_of == "Textbook" %>selected<% end %>>Textbook</option>
<option value="Magazine" <% if product.type_of == "Magazine" %>selected<% end %>>Magazine</option>
<option value="Book" <% if product.type_of == "Book" %>selected<% end %>>Book</option>
</select>
When I have the code like this, I get the error:
undefined method `type_of' for #<NestedForm::Builder:0x00000102304f78>
but if I change the render line to (I just made it less explicit by taking out partial:):
<%= render "edit_product_fields", locals: {product:product} %>
I get the error:
undefined local variable or method `product' for #<#<Class:0x0000010235a248>:0x0000010684b3c0>
In your first code block, you have a Builder object being stored as product.
Fortunately, Builder provides an object method so you can access the actual object that it's representing in the form:
<select>
<option value="Textbook" <%= 'selected' if product.object.type_of == "Textbook" %>>Textbook</option>
<option value="Magazine" <%= 'selected' if product.object.type_of == "Magazine" %>>Magazine</option>
<option value="Book" <%= 'selected' if product.object.type_of == "Book" %>>Book</option>
</select>
Well type_of is not a ruby method. It looks like it's an attempt to type check though... But type checking is generally frowned upon in duck-typed languages. At any rate, we can take advantage of a Rails Form Helper instead and make this code a one-liner. But first...
The reason the render is complaining about undefined local variable or methodproduct'` after your change is because the render call expects either the first argument to be a partial name and the second to be a hash of locals:
<%= render "partial_name", local1: "a", local2: "b" %>
... or it expects the the whole thing to be a hash:
<%= render partial: "partial_name", locals: { local1: "a", local2: "b" } %>
Now, back to the form helper.
What you're looking for here is the select helper. This makes the following possible:
<%= product.select :type_of, %w[Textbook Magazine Book] %>
And this will render all of the same html as you've put into your _edit_product_field.html.erb partial. So you should probably just get rid of the partial to save on rendering time and put this directly into your form.
One other, minor point. It may be less confusing to call the fields_for block variable product_form instead of product. This is to help keep in mind that this is not a Product object you're dealing with, but rather a Form Generator object. Also, you don't need to specify :multipart => true in Rails 4 forms -- Rails knows to make the form multi-part if you have a file upload element within the form body.
So putting it all together:
<%= form_for #store do |f| %>
<%= f.fields_for :products do |product_form| %>
<%= product_form.select :product, %w[Textbook Magazine Book] %>
<% end %>
<%= f.submit %>
<% end %>
Related
Users apply filters in an index page and I need the filters' params to do some background jobs.
I can access the params after the filters are applied as you normally would, but when I try to forward the request.params to the controller action where I launch the background jobs, the params passed in the form's hidden_field no longer is an ActionController::Parameters object but a String instead, which I can't access as I would normally do by it's key.
form:
<%= form_tag admin_users_on_demand_mass_emails_path, method: :get do %>
<%= simple_fields_for :on_demand_email do |f| %>
<%= hidden_field_tag :filters, params[:q].to_unsafe_h %>
<%= f.input :email,
as: :radio_buttons,
collection: options_for_on_demand_emails %>
<% end %>
<%= submit_tag "Enviar emails", class: "btn btn-primary" %>
<% end %>
params before form send:
<ActionController::Parameters {"q"=><ActionController::Parameters {"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont"=>"", "extra_params"=><ActionController::Parameters {"invoice_status"=>""} permitted: false>, "subscription_payment_type_eq"=>"", "address_province_eq"=>"", "subscription_status_eq"=>"cancelled", "created_at_gteq"=>"", "created_at_lteq"=>""} permitted: false>, "subscription_status_eq"=>"{}", "commit"=>"Filter", "controller"=>"admin/users", "action"=>"index"} permitted: false>
becomes:
{"filters"=>
"{\"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont\"=>\"\", \"extra_params\"=>{\"invoice_status\"=>\"\"}, \"subscription_payment_type_eq\"=>\"\", \"address_province_eq\"=>\"\", \"subscription_status_eq\"=>\"cancelled\", \"created_at_gteq\"=>\"\", \"created_at_lteq\"=>\"\"}",
"on_demand_email"=>"[FILTERED]",
"commit"=>"Send emails"}
I've tried many different things such as passing request.params.to_unsafe_h (same result) and params.require(:q).permit(params[:q].keys) which results in an error in the view when :q is not present due to the require.
Is there any way to accomplish this or rebuild the params hash after it's been passed as a string?
You can use JSON.parse() to convert them into hash like below,
{"filters"=>
JSON.parse("{\"fname_or_lname_or_email_or_tax_id_number_or_legal_name_cont\"=>\"\", \"extra_params\"=>{\"invoice_status\"=>\"\"}, \"subscription_payment_type_eq\"=>\"\", \"address_province_eq\"=>\"\", \"subscription_status_eq\"=>\"cancelled\", \"created_at_gteq\"=>\"\", \"created_at_lteq\"=>\"\"}"),
"on_demand_email"=>"[FILTERED]",
"commit"=>"Send emails"}
In the end I did params.permit(q: params.dig(:q)&.keys
For my ERB, if there are checkboxes with same values but for different forms, after a new render due to create error the checkboxes for different forms with same value gets checked. It doesn't seem to matter their different ids.
I have several checkboxes for model Option with two columns :size and :color. Currently, I'm using a bigger model to accept_nested_attributes for the Option model.
In my form, for each Option, I'm putting a checkbox with hidden value for color and checkbox for size such that:
<%= form_for #big do |big| %>
<%= f.fields_for :options do |option| %>
<%= f.hidden_field :color, value: "Red" %>
<%= f.check_box :size, {}, "Small", nil %>
<% end %>
<%= f.fields_for :options do |option| %>
<%= f.hidden_field :color, value: "Blue" %>
<%= f.check_box :size, {}, "Small", nil %>
<% end %>
.....
<% end %>
The options above have each unique ids according to html and everything but just gets checked equally, which I suspect is due to the same value.
Is there any way to prevent this behaviour?
I solved the problem.
This was more deeply rooted into my shabby controller practice mixed with overusing ERB fields_for.
So for my controller, I only have one #bigmodel.options.build, and in the controller I have four f.fields_for option. The problem was that the big model had one association to the options and therefore all options were being treated equally, regardless of the html output.
Since the html output was unique, the value submission was okay - not buggy - but when something failed server side, there were myriad of problems bound to come out to html.
I have the following code which displays a dropdown menu (I have it this way the so the form shows the last selected option). I would like to pass the value associated with the selection to a method in my controller.
I want to save this to the the value of "choice" which is specified as an integer in my user model. The initial value of choice is 0.
I can't seem to find a way to do this with the local "value".
<select class="form-control">
<option value= 1>One</option>
<option value= 2>Two</option>
<option value= 3>Three</option>
</select>
<%= link_to "Select", {controller: :controller_name, action: :method_name, value: :value} %>
Controller method (I am using Devise to call "current_user")
def method_name
current_user.choice = value
current_user.save
redirect_to root_path
end
Try a form, here is an example of what you need below:
<%= form_tag(url_for(controller: 'controller_name', action: 'method_name'), method: :method_type(PUT, POST, etc.) do %>
<div class="form-group">
<%= label_tag(:choice, "Choice") %>
<%= select_tag(:choice, options_for_select([0, 1, 2, 3], selected: 0)) %>
</div>
<%= submit_tag 'Save'%>
<% end %>
As a commenter eluded to, in your method you can then use:
def method_name
current_user.choice = params[:choice]
current_user.save
redirect_to root_path
end
When writing an HTML file, why use <%= INSERT RAILS HERE %> vs. <% INSERT RAILS HERE %>
<%= %> emits a string, <% %> runs code.
On the pedantic side, you're writing an ERb template, not an HTML file--the syntax is the same whether it's a template for HTML, JS, or whatever.
The ERB docs provide additional (but not complete) information.
<%= %> will return value and display in your page. Assume that you have person.name = 'Dark'
<%= person.name %>
will display Dark in your web page.
<% %> will not return any value to your page. It just embed simple ruby code. Usually used with `control statement'.
<% if person.present? %>
<span><%= person.name %></span>
<% end %>
When we use <%= %> it simply displays the value returned, on the html page.
<% %> executed the code but doesn't dispaly it on the html page.
How do I place a link at the top of my page when the URL that it is pointing to is not determined until later down the page. In this example, I want to move Create and Edit Scenario links to the top of the page, but as you can see Edit Scenario depends on knowing the #scenario_id first.
<%= will_paginate #scens, :next_label => 'Older', :prev_label => 'Newer' %>
<div class="box">
<% for scenario in #scens %>
<% #created = scenario.created_at %>
<% #updated = scenario.updated_at %>
<% #scenario_id = scenario.id %>
<% if scenario.scenario_image.exists? %>
<%= scenario_image_tag(scenario) %>
<% end %>
<%= simple_format(scenario.description) %>
<% end %>
</div>
<% if session[:role_kind] == "controller" %>
<p>
<%= button_to "Create new scenario", :action => "create" %>
<% if #scens.size > 0 %>
<%= button_to "Edit scenario", :action => "edit", :id => #scenario_id %>
<% end %>
</p>
You can add the link at the top but you will need to programmatically access it later and then assign the URL to it. That needs some kind of reference or look-up capability, I'm thinking client-side javascript but that's as I don't know Ruby.
Alternatively you could create the link later when you have the URL and place the link at the top using CSS positioning. The actual position of all the DOM elements on the page need not match the order in which they are rendered.
One way to do this is to use a helper:
In your helper.rb file:
def stack_example(scens, &block)
html = 'Scenario Details'
edit_link = 'Edit Link'
yield html, edit_link
end
Then in your partial you could have something like:
<% stack_example(#scens) do |html, edit_link| %>
<%= edit_link %><br>
<%= html %>
<% end %>
Should output the following:
Edit Link
Scenario Details
I don't get it. Why do you create model in the view layer? Why wouldn't you create the model variables in the controller? Sth like:
class your_controller
def your_method
#scenario_id = ...
end
end
I think that your problem lays in the invalid MVC usage. Don't you think that all the #member #variables should be initialized before the view starts to render?