I'm trying to DRY up one of my forms that sometimes has additional fields (for nested models) when making a new entry, but wouldn't need those fields when performing an update. As such, I was trying to pass the additional fields in via a block, but the form object isn't being passed properly.
Is there a way to pass a form object (or really any variable) into a yield?
Example code for reference:
_form.slim
= nested_form_for #model do |f|
.row
= f.label :name
= f.text_field :name, autofocus: true
...
= yield
...
= f.submit 'Save'
new.html.slim
== render layout: 'model/form' do
h3 Additional Fields
= f.fields_for :nested do |h|
= a.label :name, 'Nested Name'
= a.text_field :name
= a.link_to_remove do
= fa_icon 'times-circle-o'
= f.link_to_add "Add another nested model", :nested
edit.html.slim
== render layout: 'model/form'
To elaborate on my comment, this is how I'd do it using partials:
_form.slim
= nested_form_for #model do |f|
.row
= f.label :name
= f.text_field :name, autofocus: true
...
- if defined?(additional_fields)
h3 Additional Fields
= f.fields_for :nested do |h|
= a.label :name, 'Nested Name'
= a.text_field :name
= a.link_to_remove do
= fa_icon 'times-circle-o'
= f.link_to_add "Add another nested model", :nested
...
= f.submit 'Save'
new.html.slim
== render 'model/form', :additional_fields => true
edit.html.slim
== render 'model/form'
I might be missing something, but I'm not sure why this wouldn't work.
Related
I am using Cocoon for nested forms. For task records that are already created, I want description to be read_only.
projects/_form::
= simple_form_for #project do |f|
= f.input :name
= f.input :description
%h3 Tasks
#tasks
= f.simple_fields_for :tasks do |task|
= render 'task_fields', f: task
.links
= link_to_add_association 'add task', f, :tasks
= f.submit
And _task_fields :
.nested-fields
- if #task.description?
= f.text_field :description, disabled: true
- else
= f.input :description
= f.input :done, as: :boolean
= link_to_remove_association "remove task", f
Currently the validation in _task_fields is not working: undefined method <description> for nil:NilClass in the line with if statement. How can I write the IF statement correctly?
For task records that are already created, I want description to be
read_only.
You are doing it wrong. You should be able do the check by using new_record? like so
.nested-fields
- unless f.object.new_record?
= f.text_field :description, disabled: true
- else
= f.input :description
= f.input :done, as: :boolean
= link_to_remove_association "remove task", f
Also, by using disabled: true, the value of description won't be submitted and cannot be passed through params. If you want to have the description value in the params, use readonly: true instead.
.nested-fields
- unless f.object.new_record?
= f.text_field :description, readonly: true
- else
= f.input :description
= f.input :done, as: :boolean
= link_to_remove_association "remove task", f
Rails 3.2
Ruby 2.15
I have a somewhat complex view app/views/tickets/show.html.slim. Inside this view, I render various sections of the the ticket
One section is called customer_info. Here's what I have:
render 'tickets/sections/customer_info', locals: { customer_info: CustomerInfo.new, ticket: #ticket }
In my app/views/tickets/sections/_customer_info.html.slim, I have:
= form_for(customer_info) do |f|
- :ticket_id = ticket.id
= f.hidden_field :ticket_id
.form-horizontal-column.customer-info
.form-group
= f.label :first_name
= f.text_field :first_name
.form-group
= f.label :last_name
= f.text_field :last_name
.actions = f.submit 'Save'
.clear
I am however getting the following error message:
_customer_info.html.slim:2: syntax error, unexpected '=', expecting keyword_end
; :ticket_id = ticket.id;
^
I am starting to learn .slim. Any ideas?
You can't set a symbol to something - they are immutable. You can remove the 2nd line do do something like:
= form_for(customer_info) do |f|
= f.hidden_field :ticket_id, :value => ticket.id
...
Let's say I have Workout model that has many Item. Also Workout accepts nested attributes for Items. Using form_for I want to send parameters of item inside an array in the following manner:
"workout"=>{"workout_title"=>"title",
"items"=>{
["name"=>"first item", "name"=>"second item"]
}
}
But for some reason I was only able to get it like this
"workout"=>{"workout_title"=>"title",
"items"=>{"name"=>"second_item"}
}
How can I solve this?
Here's the form that produces the above parameters.
= form_for :workout, url: welcome_index_path, method: :post do |f|
= f.text_field :workout_title
= f.fields_for :items, child_index: 1 do |builder|
= builder.text_field :name
= f.fields_for :items, child_index: 2 do |builder|
= builder.text_field :name, id: 'workout_items_name_2', name: 'workout[items][name]'
= f.submit 'Go'
Why not let the fields_for generate all the items for you?
= form_for #workout, url: welcome_index_path, method: :post do |f|
= f.text_field :title
= f.fields_for :items do |builder|
= builder.text_field :name
= f.submit 'Go'
It would display text fields for all your items in #workout. Also, you would receive items in params[:workout][:item_attributes]. Plus, Rails' #post.save(params[:workout]) would also create related items as well.
I've just started a new app where I want to take a postcode in a form and save it to the database. My problem is that the create action doesn't seem to be being called no matter what I try.
Routes:
root 'postcodes#new'
resources :postcodes, only: [:new ,:create]
Controller: postcodes_controller.rb
class PostcodesController < ApplicationController
def new
#postcode = Postcode.new
end
def create
#postcode = Postcode.new(postcode_params)
if #postcode.save
flash[:success] = 'Success'
else
flash[:error] = 'Error'
end
end
private
def postcode_params
params.require(:postcode).permit(:code)
end
end
Model: postcode.rb
class Postcode < ApplicationRecord
validates :code, presence: true, uniqueness: true
end
View: postcodes/new.haml
.container
%form
%fieldset.form-group
= form_for #postcode do |f|
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn btn-primary'
I've attempted to pass more options in the form_for such as the method and action and now I have a feeling it's a routing error.
Any help will be appreciated.
Thanks.
I believe the problem you are experiencing is a result of your HAML.
You do not need to use, nor should you use, a form HTML element outside the form_for method call.
The form_for method will handle generating this HTML element/tag for you.
You have:
.container
%form
%fieldset.form-group
= form_for #postcode do |f|
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn ban-primary'
Which outputs an empty <form> element.
You should have:
.container
= form_for #postcode do |f|
%fieldset.form-group
= f.label :postcode
= f.text_field :code, placeholder: 'Example Postcode', class: 'form-control'
= f.submit 'Submit', class: 'btn ban-primary'
That should generate a proper <form> tag with the required action and method attributes populated with the right URL and 'post' so that your create action is called.
in my app I have form that looks like this
= simple_form_for #user do |f|
= f.input :name, error: false
= f.input :surname, error: false
Is there any way to avoid this repetitions (error: false)?
If they're all of the same type, something like this should work:
= simple_form_for #user do |f|
- [ :name , :surname ].each do |field|
= f.input field, error: false
If not, you could use a hash or something, instead of an array, and specify the type, as well.
It appears that simple form has the following option:
If you want to pass the same options to all inputs in the form (for
example, a default class), you can use the :defaults option in
simple_form_for. Specific options in input call will overwrite the
defaults:
<%= simple_form_for #user, defaults: { input_html: { class: 'default_class' } } do |f| %>
<%= f.input :username, input_html: { class: 'special' } %>
<%= f.input :password, input_html: { maxlength: 20 } %>
<%= f.input :remember_me, input_html: { value: '1' } %>
<%= f.button :submit %>
<% end %>
From https://github.com/plataformatec/simple_form
So, in your case:
= simple_form_for #user , defaults: { error: false } do |f|
= f.input :name
= f.input :surname
You could loop through an array of symbols
simple_form_for #user do |f|
[:name, :surname].each do |element|
f.input element, error: false
end
end