accepts_nested_attributes_fields not showing for join table - ruby-on-rails

I have the following tables
class Region < ActiveRecord::Base
has_many :companies, through: :companies_regions
has_many :companies_regions, :dependent => :destroy
end
class Company < ActiveRecord::Base
has_many :regions, through: :companies_regions
has_many :product_type, dependent: :destroy
has_many :companies_regions, :dependent => :destroy
accepts_nested_attributes_for :companies_regions, :allow_destroy => true
end
class CompaniesRegion < ActiveRecord::Base
belongs_to :company
belongs_to :region
end
I want to create a new company and i want to be able to add a new region accordingly to the CompaniesRegion table.
form.html.erb
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.simple_fields_for :companies_regions do |company_region| %>
<%= render 'company_region', f: company_region %>
<% end %>
<%= link_to_add_association 'New Region', f, :companies_regions, partial: 'company_region' %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
_company_region.html.erb
<div class="nested-fields form-group row">
<div class="col-md-1"></div>
<div class="col-md-3">
<%#= f.select :region_id, class: 'form-control', placeholder: 'Region' %>
<%= f.input_field :region_id, collection: ['Asia', 'America'], class: 'form-control', prompt: 'Please Select' %>
</div>
<div class="col-md-1">
<%= link_to_remove_association(f, title: 'Remove') do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
</div>
</div>
The problem here is that when i click the New Region link, i am expecting it to display the details in the _company_region.html.erb but unfortunately it doesnt.
Nothing shows. It doesnt display any data. It however refreshes the page which is absurd.
Dont know if it is because my table is a join table hence the problem or if there is something else that i am missing but based on documentation, this should be fine and should work.
Any help is deeply appreciated

Its a very common misconception that you need to use nested attributes to set assocations. You don't. Rails generates a regions_ids= setter for the association that can be hooked up with checkboxes or a select tag.
All you really need with SimpleForm is:
<%= f.association :regions %>
See the docs for ActionView::Helpers::FormOptionsHelper and associations in SimpleForm for more details on how this works.
<%= simple_form_for(['admin', #company]) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<%= f.input :name %>
</div>
<div>
<div class="row">
<div class="col-md-12">
<h4>Basic Coverages</h4>
<div class="row form-group">
<label class="col-md-1">#</label>
<label class="col-md-3">Coverage</label>
<label class="col-md-1">Description</label>
</div>
<div>
<%= f.association :regions %>
</div>
</div>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
The only reason you would need to use nested attributes is if the user must be able to create regions in the same request. But usually its better to use ajax to handle those cases.

Your code looks fine. The behaviour you describe seems to indicate that the cocoon.js code is not included/loaded correctly? Did you
add require 'cocoon' in application.js ?
did you include application.js in your html?

Related

Rails 5.2: How to create fields_for each I18n key value?

In my _form.html.erb I have nested fields, where for an Offer I would like to save multiple Discount types with values:
<%= f.fields_for #offer.discounts do |discount| %>
<% I18n.t(:discount_type).each do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
At the moment my form is populated correctly as I would like it to be, however values are not saving since:
in my params I see only 1 of 3 discount types like this:
"seller_discount"=>{"discount_type"=>"special", "value"=>"5"}
there is error Unpermitted parameter: :seller_discount
records are not saving
My Seller::Offer model looks like this:
has_many :offer_discounts, class_name: "Seller::OfferDiscount"
has_many :discounts, class_name: "Seller::Discount", through: :offer_discounts, inverse_of: :offers
accepts_nested_attributes_for :discounts, allow_destroy: true
My controller is simple as:
def new
#offer = Seller::Offer.new
end
private
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
discounts_attributes: [:id, :discount_type, :value, :_destroy]
)
end
So far I've been trying different ideas from Rails docs, however no luck. Probably in my specific case, twist is where I try to iterate over I18n.t(:discount_type) an create input field for each discount type (buy key).
I'll be happy for any hint how to solve this. Thank you!
Since you're iterating over discount_type I think that needs to be an array type in your offer_params method.
def offer_params
params.require(:seller_offer).permit(
:company_id, :name, :base_price,
seller_discounts_attributes: [:id, :discount_types => [:discount_type, :value], :_destroy]
)
end
But what happens if you try to use fields_for helper?
<%= f.fields_for #offer.discounts do |discount| %>
<%= f.fields_for I18n.t(:discount_type) do |type| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= discount.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= discount.hidden_field :discount_type, value: type[0] %>
<%= discount.number_field :value,
value: (#offer.new_record? ? '0.00' : discount.value),
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
So, to have my form working for both New and Edit actions, final solution is this:
<% if params[:action] == 'new' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for new form !-->
<% I18n.t(:discount_type).each do |type| %>
<%= f.fields_for :discounts, #offer.discounts.build do |disc| %>
<div class="form-group row discount-list">
<label class="col-sm-8 control-label">
<%= disc.label I18n.t("discount_type.#{type[0]}") %><br/>
</label>
<div class="col-sm-4">
<%= disc.hidden_field :discount_type, value: type[0] %>
<%= disc.number_field :value, value: '0.00',
class: "form-control allow_numeric" %>
</div>
</div>
<% end %>
<% end %>
</div>
<% elsif params[:action] == 'edit' %>
<div class="col-md-7 col-sm-7">
<!-- Discounts for edit form !-->
<%= f.simple_fields_for :discounts do |d| %>
<div class="form-group row discount-list">
<%= d.input :discount_type, as: :hidden %>
<label class="col-sm-8 control-label">
<%= d.label I18n.t("discount_type.#{d.object.discount_type}") %><br/>
</label>
<div class="col-sm-4">
<%= d.input :value, label: false, input_html: { id: d.object.discount_type+"_discount",
class: "form-control allow_numeric" } %>
</div>
</div>
<% end %>
</div>
<% end %>
Edit action is done with simple_form_fields_for
Obviously not shiny solution, but looks like this works.

Rails how to save Address from different models (Country, State, City, Address) in one single form?

So what I'm trying to do is save in one single form a bunch of data. Part of that data is an address. This address has been split in several models: Country, State, City and Address.
My problem is that honestly I have no idea how to manage that in the controller. I will share what I have at the moment and hope someone can give me a clue.
Should I do accepts_nested_attributes_for for all models?. How can I connect all this inherits fields or foreign key in order to process the information?
Country Model:
class Country < ApplicationRecord
has_many :states
end
State Model:
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
City Model:
class City < ApplicationRecord
belongs_to :state
has_many :addresses
accepts_nested_attributes_for :addresses
end
Address Model:
class Address < ApplicationRecord
belongs_to :city
belongs_to :office
end
Office Model:
class Office < ApplicationRecord
has_many :addresses, inverse_of: :office
accepts_nested_attributes_for :addresses
end
Office Controller Strong Params for Country, State, City and Address (There are more params, I just placed those regarding to the issue I'm having):
def office_params
params.require(:office).permit(
{
countries_attributes: [
:id, :name,
states_attributes: [
:id, :name,
cities_attributes: [
:id, :name,
addresses_attributes: [
:id, :street, :state_id
]
]
]
]
},
)
Office New Method:
def new
#office = Office.new
#countries = Country.all.map{|c| [ c.name, c.id ] }
#states = State.all.map{|c| [ c.name, c.id ] }
##office.addresses.build.cities.build
##office.addresses.counties.states.cities.build
##office.addresses.build
end
Office Form: In this form I'm using fields_for in order to nested all the fields called from their respective models. So here I need to save all information at once to several models.
<%= form_with(model: office, local: true, html: {class: "form-office"}) do |form| %>
<% if office.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(office.errors.count, "error") %> prohibited this office from being saved:</h2>
<ul>
<% office.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_name %></span>
<%= form.text_field :office_name, class: 'form-control' %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_slug %></span>
<%= form.text_field :office_slug, class: 'form-control' %>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :office_email %></span>
<%= form.text_field :office_email, class: 'form-control' %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12">
<div class="form-group">
<span><%= form.label :phone %></span>
<%= form.text_field :phone, class: 'form-control' %>
</div>
</div>
</div>
<hr>
<h3 style="color: #B0B0B0;">Office Address</h3>
**Office Address**
<div class="row">
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-6">
<%= form.fields_for :countries do |country| %>
<div class="form-group">
<span><%= country.label :country %></span>
<%= select_tag(:country_id, options_for_select(#countries), class: 'form-control', :prompt => "Select Country") %>
</div>
<div class="form-group">
<%= country.fields_for :states do |state| %>
<span><%= state.label :state %></span>
<%= select_tag(:state_id, options_for_select(#states), class: 'form-control', :prompt => "Select State") %>
</div>
</div>
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-6">
<div class="form-group">
<%= state.fields_for :cities do |city| %>
<span><%= city.label :city %></span>
<%= city.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= city.fields_for :addresses do |address| %>
<span><%= address.label :street %></span>
<%= address.text_field :street, class: 'form-control' %>
</div>
<div class="form-group">
<span><%= address.label :zip_code %></span>
<%= address.text_field :zip_code, class: 'form-control' %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
</div>
</div>
<hr>
<h4 style="color: #B0B0B0;">Comment</h4>
<div class="row">
<div class="col-md-12 col-sm-6 col-xs-12 st">
<div class="comments">
<%= form.fields_for :comments do |comment_form| %>
<%= render 'comment_fields', f: comment_form %>
<% end %>
</div>
</div>
</div>
<div>
<div class="col-md-4 offset-md-4">
<%= form.submit class: 'btn btn-lg' %>
</div>
</div>
<% end %>
Yes, you have to add accepts_nested_attributes_for for every association you want to update through fields_for.
class Country < ApplicationRecord
has_many :states
accepts_nested_attributes_for :states
end
class State < ApplicationRecord
belongs_to :country
has_many :cities
accepts_nested_attributes_for :states
end
However, if you do that and if you define office_params correctly, saving all nested models is possible with single Office.create(office_params) call, without worrying about setting foreign keys.
Same goes for updating, but you will have to provide hidden :id form fields for nested models, so existing model instances can be found in DB.
You should also consider adding validates_associated to models.

How to add in multiple options for checkbox in Ruby on Rails?

This is likely a super easy answer for a Ruby on Rails expert. I have a form and need to add in a checkbox that has multiple items. I've been messing with the following code for a lot longer than I'd like to admit:
<%= form_for :lead, url: something, html: {id: 'product-form'} do |f|%>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label :product%>
<%= f.check_box :product, {multiple:true}, "option", "option2", :class => 'form-control'%>
</div>
</div>
</div>
<% end %>
With this code I get the error "wrong number of arguments (5 for 1..4)".
Basically I just want someone to be able to pick multiple options. I've also tried the following:
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label :product%>
<%= f.check_box :option1, "Option1" :class => 'form-control'%>
<%= f.check_box :option2, "Option2", :class => 'form-control'%>
</div>
</div>
</div>
And I get the delightful "undefined method `merge' for "Option1":String". What am I missing to put the values in associated with the label?
I use Rails 5.1.6 and this is how I achieved adding multiple options for checkbox. I also placed it in a dropdown.
In the migration file:
t.string :skills
In my controller "tasks_controller.rb":
def task_params
params.require(:task).permit(:work_type, :title, :post, {skills: []})
end
In my model "task.rb" I did:
validates :skills, presence: true
serialize :skills, JSON
I created a module mobilephones_data.rb in directory "app/model/concerns/" which holds all the options of the checkbox as an array that can be edited easily.
In app/model/concerns/mobilephones_data.rb I wrote:
module Mobilenphones_Data
Activities = [
'Amazon Kindle', 'Blackberry', 'iPad', 'iPhone', 'Mobile Phone', 'Nokia',
'Palm', 'Samsung'
]
end
This will put all data from module's array into the checkbox drop-down as checkbox options. In My form I did:
<div class="card">
<a class="card-link card-header" data-toggle="collapse" href="#collapseOne">
Mobile Phones
</a>
<div id="collapseOne" class="collapse" data-parent="#accordion">
<div class="card-body">
<% Mobilenphones_Data::Activities.each do |activity| %>
<div id="skill_list" class="col-lg-3 col-md-4 col-sm-12 col-12">
<%= f.check_box :skills, { multiple: true }, activity, false %>
<%= activity %>
</div>
<% end %>
</div>
</div>
</div> <!--bottom-->
</div>
In my view "show.html.erb":
<% #name_of_model.skills.each do |skill| %>
<%= skill %>
<% end %>
For posterity sake:
<div class="row">
<div class="col-md-12">
<div class="form-group">
<%= f.label "Select a Product" %><br />
<%= f.check_box(:option) %>
<%= f.label(:mug, "Option") %><br />
<%= f.check_box(:option2) %>
<%= f.label(:mousepad, "Option2") %>
</div>
</div>
</div>

link_to_remove_association: undefined method `new_record?' for nil:NilClass

I'm trying to build 2 nested forms with Cocoon which works fine. I have a Game model which has many Questions and a Question has many Answers.
Models
# Game model
class Game < ApplicationRecord
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions, allow_destroy: true
end
# Question model
class Question < ApplicationRecord
belongs_to :game
has_many :answers, dependent: :destroy
validates :text, presence: true
accepts_nested_attributes_for :answers, allow_destroy: true
end
# Answer model
class Answer < ApplicationRecord
belongs_to :question
end
the link_to_add_association link works everywhere, the link_to_remove association link works fine in _question_fields partial view but I have
`"undefined method `new_record?' for nil:NilClass"`
error when I insert the link_to_remove_association in _answer_fields partial view. I'm sure that it's an error on my part but I can't retrieve where.
error
Views
new.html.erb
<%= form_with(model: #game, url: games_path, local: true ) do |form| %>
<div class="form-group game">
<%= form.label(':name', "Title") %>
<%= form.text_field ':name', class: 'form-control form-control-sm' %>
</div>
<div class="wrapper-questions"></div>
<%= form.fields_for :questions do |question_form| %>
<%= render 'games/partials/question_fields', f: question_form %>
<% end %>
<div class="row form-actions links">
<div class="col-sm">
<%= link_to_add_association 'Add a question',
form,
:questions,
:partial => 'games/partials/question_fields',
class: "btn btn-secondary",
'data-association-insertion-node' => '.wrapper-questions'
%>
</div>
<div class="col-sm text-right">
<%= form.submit 'Save', :class => "btn btn-primary" %>
</div>
</div>
<% end %>
partials/_question_fields.html.erb
<div class="question-wrapper jumbotron nested-fields">
<%= link_to_remove_association f, { class: 'btn btn-light' } do %>
<i class="fa fa-trash" aria-hidden="true"></i> Delete the question
<% end %>
<p><strong>Question</strong></p>
<div class="form-group">
<strong><%= f.label(:text, 'Question:') %></strong>
<%= f.text_field :text, class: "form-control" %>
</div>
<%= f.fields_for :questions do |answer_form| %>
<%= render 'games/partials/answer_fields', f: answer_form %>
<% end %>
<div class="row">
<div id="wrapper-answers"></div>
<div class="col-sm">
<%= link_to_add_association 'Add an answer',
f,
:answers,
:partial => 'games/partials/answer_fields',
data: { 'association-insertion-method' => :prepend },
class: "btn btn-secondary"
%>
</div>
</div>
</div>
partials/_answer_fields.html.erb
<div class="row nested-fields">
<div class="col">
<%= f.text_field :text, class: "form-control" %>
</div>
<div class="col">
<label class="btn btn-secondary form-control">
<%= f.check_box :correct %>
</label>
</div>
<!-- My error is here -->
<%= link_to_remove_association 'remove answer', f %>
</div>
Does anybody have an idea what I'm doing wrong ?
Regards,
Lilith
In partials/_question_fields.html.erb
<%= f.fields_for :questions do |answer_form| %>
Should likely be :
<%= f.fields_for :answers do |answer_form| %>

Placing data from associated table in view

I have a class called Imprintable that contains this
class Imprintable < ActiveRecord::Base
has_one :brand
# ...
I also have a class called Style that contains the following:
class Style < ActiveRecord::Base
belongs_to :imprintable
# ...
My schema for Styles contains a foreign key to the imprintable table in the form of an integer called imprintable_id
I'm trying to display an attribute from the Style table called catalog_no in a view to edit information about an imprintable. I know what I have is wrong because style doesn't exist as a member of the imprintable table, but I'm not sure how to access the name of the catalog_no from the corresponding entry in the styles table. HTML is like this:
<!-- language: HTML -->
<div class="box-info">
<%= render partial: 'shared/modal_errors', locals: {object: imprintable} %>
<%= form_for(imprintable) do |f| %>
<div id="horizontal-form" class="collapse in">
<!-- Lots of HTML.. -->
<div class="form-group">
<%= f.label :style.catalog_no, class: 'col-sm-2 control-label' %>
<div class="col-sm-10">
<%= f.text_field :style.catalog_no, class: 'form-control' %>
<!-- Problem is on the above line!! -->
<p class="help-block">The catalog number of the imprintable
</div>
</div>
</div>
<% end %>
</div>
Thanks for your time!
You should use nested form
Model Imprintable looks like this :
class Imprintable < ActiveRecord::Base
has_one :brand
accepts_nested_attributes_for :style
end
In controller edit action
def edit
#imprintable = Imprintable.find(params[:id])
end
In the view file edit.html.erb looks like
<div class="box-info">
<%= render partial: 'shared/modal_errors', locals: {object: imprintable} %>
<%= form_for(#imprintable) do |f| %>
<div id="horizontal-form" class="collapse in">
<%= f.fields_for :style do |d| %>
<div class="form-group">
<%= d.label :catalog_no, class: 'col-sm-2 control-label' %>
<div class="col-sm-10">
<%= d.text_field :catalog_no, class: 'form-control' %>
<p class="help-block">The catalog number of the imprintable
</div>
</div>
<% end %>
</div>
<% end %>
</div>
If you're using rails 4, don't forget add style_attibutes to impritable_params method
private
def impritable_params
## params.require(:your_model).permit(:fields_of_model, association_model_attributes: [:fields_of_association_models])
params.require(:imprintable).permit(style_attributes: [:id, :catalog_no])
end
end

Resources