Cocoon and has_one association - ruby-on-rails

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

Related

How to get email member on simple_form from wallet model? [complicated relation]

I have problem. I'm trying to get an email member from wallet_id which that relation not direct to member instead to investor account first like the sample model below.
member.rb
class Member < ActiveRecord::Base
has_one :investor_account, dependent: :destroy
end
investor_account.rb
class InvestorAccount < ActiveRecord::Base
belongs_to :member
has_many :wallets, dependent: :destroy
end
wallet.rb
class Wallet < ActiveRecord::Base
belongs_to :investor_account
end
top_up.rb
belongs_to :wallet
/top_ups/_form.html.slim
= simple_form_for [:transaction, #top_up] do |f|
.form-group
= f.input :wallet_id, collection: #wallet, input_html: { class: 'form-control' }, include_blank: "Select..."
.form-group
= f.input :amount, input_html: { min: 0, value: 0 }
/controllers/top_ups_controller.rb
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'})
end
The data on "f.input :wallet_id ...." was showed up, but it is not as email of the member instead it showing #<Wallet:0x007fd6d795e808> on all wallet dropdown, previously I also write code like below.
= f.input :wallet_id, collection: #wallet, :id, :email, input_html: { class: 'form-control' }, include_blank: "Select..."
But it throwing problem the email not found. My question was how do I pass the member on that #wallet = ... variable to get email member showing on my form? Is there better way to get that?
You can use label_method and value_method params (docs):
= f.input :wallet_id, collection: #wallet, value_method: :id, label_method: :email, input_html: { class: 'form-control' }, include_blank: "Select..."
Also if you need only id and email, there is no need to fetch all the other data from the DB, you can use pluck:
# controller
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'}).pluck(:id, :email)
end
# form
= f.input :wallet_id, collection: #wallet, value_method: :first, label_method: :last, input_html: { class: 'form-control' }, include_blank: "Select..."

rails form send many rows from same instance

I have a form to create a teacher, and in this form a have a table where I register the subjects that they can teach and some experience related of it.
But I dont know how to do it, in model, in controller and in view, because a have add nested fields to my teacher form, and use nested_attributes in model, and permit it in controller. but I dont know how to do it..
Model
class Tutor < User
has_many :tutor_subjects
accepts_nested_attributes_for :tutor_subjects
end
In Controller i have the functions...
def create
#tutor = Tutor.new(tutor_params)
#tutor.save
end
def tutor_params
params.require(:tutor).permit(:email,
:first_name,
:last_name,
:gender,
:password,
tutor_subjects_attributes: [])
end
end
And the view
= simple_form_for [:admin, #tutor] do |f|
= f.error_notification
.col-md-8.col-sm-12
.form-inputs
= f.input :email
= f.input :password
= f.input :first_name
= f.input :last_name
= f.input :gender, collection: {Male: 1, Female: 2}, include_blank: "--Select gender--"
%h2.well.well-sm Subjects
%table.table
%thead
%tr
%th Subject
%th Experience
%th Options
%tbody#subjects
= f.simple_fields_for :tutor_subjects_attributes do |s|
%tr.subject0
%td
= s.input :subject_id, collection: Subject.all, label: false, include_blank: "--Select subject--", input_html: { id: "tutor_subjects_attributes_0_subject_id", name: 'tutor[tutor_subjects_attributes][0][subject_id]', class: "subject" }
%td
= s.input :experience, as: :text, label: false, input_html: { placeholder: "Type your experience", name: 'tutor[tutor_subjects_attributes][0][experience]', id: "tutor_subjects_attributes_0_experience" }
%td.options
= link_to :add_subject, "#", id: "add_subject", class: "btn btn-small btn-info"
I am not sure I understand your question right but it seems like you have problems dealing with strong parameters for nested attributes.
Your tutor_params should look like
def tutor_params
params.require(:tutor).permit(
:email,
...
tutor_subjects_attributes: [
:subject_id,
:experience
]
)
end
end

Strong parameters not whitelisting attributes with simple_form

I'm having some issues understanding how to nest 3 models. I'm trying to, at the deepest point of the relations, add a Video to the WorkoutSteps (not creating a new video but select an existing one from a dropdown)
Models:
Workout
WorkoutSet
WorkoutStep -> just an array of Video
Video
Relationships
Workout:
has_and_belongs_to_many :workout_sets, :join_table => :workout_sessions, dependent: :destroy
WorkoutSet
has_and_belongs_to_many :workout_steps, :join_table => :sets_steps, dependent: :destroy
WorkoutStep
has_and_belongs_to_many :workout_sets, :join_table => :sets_steps
And the following in the views:
_form.html.haml
= simple_form_for(#workout, url: admin_workouts_path(#workout)) do |f|
= f.input :title
%h3 Sets
.sets.some{ :style => "margin-left: 25px" }
= f.simple_fields_for :workout_sets do |set|
= render 'workout_set_fields', f: set
.links
= link_to_add_association 'add set', f, :workout_sets
= f.submit
_workout_set_fields
= f.label :title
= f.text_field :title
%br
%br
#sets.some{ :style => "margin-left: 25px" }
= f.simple_fields_for :workout_steps do |step|
= render 'workout_step_fields', f: step
.links
= link_to_add_association 'add step', f, :workout_steps
_workout_step_fields
= f.association :main_videos, include_hidden: false
workouts_controller.rb
def workout_params
params.require(:workout).permit(:title, :pro, :workout_step_id, workout_sets_attributes: [ :id, :_destroy, :title, workout_steps_attributes: [ main_video_ids: [:id] ] ])
end
Checking the params:
FYI, the problem was solved as I stated on the comment above.
params.require(:workout).permit(:title, :pro, :workout_step_id, workout_sets_attributes: [ :id, :_destroy, :title, workout_steps_attributes: [ {main_video_ids: []} ]])
for more info, check this link
main_video_ids is an array attribute, not a hash of ids. So you can whitelist it directly:
def workout_params
params.require(:workout).permit(:title, :pro, :workout_step_id, workout_sets_attributes: [ :id, :_destroy, :title, workout_steps_attributes: main_video_ids: [] ] ])
end

Form for a has_many :through relation with additional attribute in join table

A question has_many kpi through kpiquestions:
class Question < ActiveRecord::Base
has_many :kpiquestions, dependent: :destroy
has_many :kpis, through: :kpiquestions
end
class Kpi < ActiveRecord::Base
has_many :kpiquestions, dependent: :destroy
has_many :questions, through: :kpiquestions
end
class Kpiquestion < ActiveRecord::Base
belongs_to :kpi
belongs_to :question
end
Now I have a working question form where I can add KPI's by using checkboxes:
= form_for #question do |f|
= render "layouts/form_messages", target: #question
.form-group
= f.label "Name"
= f.text_field :name
.form-group
= f.label "KPI's"
%ul
= hidden_field_tag "question[kpi_ids][]", nil
- #kpis.each do |kpi|
%li.checkbox
= check_box_tag "question[kpi_ids][]", kpi.id, #question.kpi_ids.include?(kpi.id), "data-category" => kpi.id
= kpi.name
.form-group
= f.submit(class: 'btn btn-default', value: "Save")
But the join table kpiquestion has an additional attribute weight. This attribute should also be added to the form, with a text_field_tag. Something like this:
= text_field_tag "question[kpi_ids][:weight]", kpi.weight, #question.kpi_ids.include?(kpi.id), "data-category" => kpi.weight
Obviously this doesn't work, because it now returns an error saying kpi has no attribute called weight. So how can I add this attribute to the form?
One way would be to make the entire form_for into a form_tag that passes to a post request in the relevant controller, then the only thing limiting which parameters you can pass would be the the params you've chosen to allow controller-side. Obviously this is against convention, but it works great for cases like this.
Here is what the code would change to:
= form_tag question_index_path, :method => :post do
= render "layouts/form_messages", target: #question *
.form-group
= label_tag "Name"
= text_field_tag :name
.form-group
=label_tag :weight
=text_field_tag :weight
.form-group
=label_tag "KPI's"
= hidden_field_tag "question[kpi_ids][]", nil
- #kpis.each do |kpi|
%li.checkbox
= check_box_tag "question[kpi_ids][]", kpi.id, #question.kpi_ids.include?(kpi.id), "data-category" => kpi.id
= kpi.name
.form-group
=submit_tag("Save", class: btn btn-default)

Rails 4 Nested Resources, Forms, strong params

I am trying to create a workout tracker. I have these nested resources
Routes.rb
resources :days do
resources :workouts
resources :meals
end
My models look fine:
Workout.rb
class Workout < ActiveRecord::Base
has_many :exercises
belongs_to :day
end
Day.rb
class Day < ActiveRecord::Base
has_many :workouts
has_many :meals
accepts_nested_attributes_for :workouts, :allow_destroy => true
accepts_nested_attributes_for :meals, :allow_destroy => true
end
The issue lies when I try to create a new workout..
../views/workouts/_form.html.haml
= form_for([#day, #workout]), html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.hidden_field :day_id, value: params[:day_id], input_html: { class: "form-control"}
= f.submit
I cant seem to save the :day_id the workout is associated with the workout though using strong params to receive them.
WorkoutsController.rb
private
def workout_params
params.require(:workout).permit(:day_id, :name, :mood)
end
def find_day
#day = Day.find(params[:day_id])
end
Instead what gets saved is a nil for :day_id
Rails Console:
Workout id: 11, name: "Hey", mood: "no", day_id: nil, created_at: "2014-12-19 14:53:29", updated_at: "2014-12-19 14:53:29"
HELP?
PS - i tried to do
= form_for([#day, #workout]), as: :foo, html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.hidden_field :day_id, value: params[:day_id], input_html: { class: "form-control"}
= f.submit
and then
def workout_params
params.require(:foo).permit(:day_id, :name, :mood)
end
But it just kept saying the :foo params where empty
The way your routes are set up, I am assuming that the day_id is being passed as a url params:
/days/:day_id/workouts
If that is the case, then you do not need to worry about #day in the form, nor the day_id in the workout_params
Form:
= form_for #workout, html: {class: 'form-horizontal'}) do |f|
= f.input :workout, label: "What did you work out", input_html: { class: "form-control"}
= f.input :mood, label: "How do you feel", input_html: { class: "form-control"}
= f.submit
Controller:
def create
#workout = #day.workouts.new(workout_params)
....
end
private
def workout_params
params.require(:workout).permit(:name, :mood)
end
def find_day
#day = Day.find(params[:day_id])
end

Resources