How to get an array of objects from form? - ruby-on-rails

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.

Related

Rails Cocoon read only fields

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 simple_form create form when column type is JSON

I have a model (FooBar) with three columns:
Foo -> String
Bar -> JSON
Baz -> String
I want to create a form for this model
Bar has default attributes of: {zing: {}, zaz: {}, laz: {}}
I would like to have the following inputs:
f.input :foo
f.input :zing
f.input :zaz
f.input :laz
f.input :baz
I tried to do this using fields_for and passing in each key and converting it to a symbol:
bar.each do |k,v|
f.input k.to_sym
end
but the error I'm getting is that FooBar has undefined method of :zaz
Any ideas would be appreciated, thanks.
You should be able to do it like this:
f.simple_fields_for :bar do |bar_f|
bar.each do |k,v|
bar_f.input k.to_sym
end
end
Don't forget to allow the parameters in the controller.
You can do something like this:
class User < ActiveRecord::Base
serialize :preferences, HashSerializer
store_accessor :preferences, :blog, :github, :twitter
end
And then you will have access to blog, github and twitter just as if they were normal properties in the model and your form is going to look something like this:
= simple_form_for(#user, html: { class: "form" }) do |f|
= f.input :blog
= f.input :github
= f.input :twitter
You have more info in this link! https://github.com/plataformatec/simple_form/wiki/Nested-inputs-for-key-value-hash-attributes
Hope it helps!
Set #temp variable
#temp = FooBar.new
#temp.data = {zing: "", zaz: "", laz: ""}
This code works for me
<%= simple_form_for #temp do |f| %>
<%= f.simple_fields_for :data do |data_f| %>
<% #temp.data.each do |k,v| %>
<%= data_f.input k.to_sym %>
<% end %>
<% end %>
<%= f.button :submit %>
<% end %>
Don't forget about permission params
params.require(:temp).permit(data: [:zing, :zaz, :laz])
If you don't want to define accessors, you could do something like:
= simple_form_for(#foo_bar) do |f|
= f.simple_fields_for :bar do |bf|
= bf.input :zing, input_html: { value: f.object.bar[:zing] }
= bf.input :zaz, input_html: { value: f.object.bar[:zaz] }
= bf.input :laz, input_html: { value: f.object.bar[:laz] }
You would need to initialise bar with {} in your controller

Rails edit serialized JSON data

I have a column that stores JSON data. I don't know how to show it when it is on Edit state.
serialize :value, JSON
= f.fields_for :value do |ff|
.form-group
= ff.label :short
= ff.text_field :short, class: 'form-control'
.form-group
= ff.label :long
= ff.text_field :long, class: 'form-control'
In place of
= f.fields_for :value do |ff|
please use the following code:
= f.fields_for :value, OpenStruct.new(#object.value) do |ff|
You will need to replace #object with your model object.

Cocoon and has_one association

I have searched on stack overflow and google to no avail.
I have a person who has_one next_of_kin
I can create a person with a nested form (with cocoon) and it saves perfectly. For some reason when I then go to the edit page it deletes the associated next_of_kin record. It renders the fields populated with the record's data, but the actual record in the database gets deleted.
My form
.full-width-row
= simple_form_for #person, url: {action: action}, wrapper: 'two_column_form' do |f|
.columns.medium-4
h4 = heading
.columns.medium-8
= f.button :submit, 'Save', class: 'right button tiny radius'
.columns.medium-12
.row
.medium-8.columns
= f.input :first_name
= f.input :last_name
= f.input :email
br
h6 Next of Kin
br
= f.simple_fields_for :next_of_kin do |nok|
= render 'next_of_kin_fields', f: nok
.link
= link_to_add_association "Add Next of Kin", f, :next_of_kin, class: 'button secondary tiny next_of_kin_button'
hr
My _next_of_kin_fields partial
.nested-fields
= f.input :first_name
= f.input :last_name
= f.input :relationship, as: :select, collection: NextOfKin::RELATIONSHIP
= f.input :telephone
= link_to_remove_association "Remove next of kin", f, class: 'remove-association button tiny alert'
My Person model:
class Person < ActiveRecord::Base
has_one :next_of_kin, dependent: :destroy
accepts_nested_attributes_for :next_of_kin, allow_destroy: true
end
My Next_of_kin model:
class NextOfKin < ActiveRecord::Base
belongs_to :person
RELATIONSHIP = [ "Mother", "Father", "Brother", "Sister", "Aunt", "Uncle", "Spouse", "Other"]
end
How do I stop it from deleting the next_of_kin record when I visit the edit page?
set force_non_association_create in link_to_add_association to true to avoid this
= link_to_add_association "Add Next of Kin", f, :next_of_kin, force_non_association_create: true, class: 'button secondary tiny next_of_kin_button'
Cocoon's documentation for this parameter: https://github.com/nathanvda/cocoon#force_non_association_create

Rails 4 yield additional form fields

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.

Resources