simple_nested_form_for not showing association validation with select2 - ruby-on-rails

I am using simple_form along with the nested_form gems. The simple_form validations are show for the parent form but are not showing for an association within the nested form.
View
#_form.html.erb
<%= simple_nested_form_for( #repair ) do |r| %>
....
<%= r.simple_fields_for :repair_items do |f| %>
<%= f.association :repair_type, label_method: :repair_type_label, value_method: :id, include_blank: true, label: "Type"%>
Model
#repair_item.rb
validates :repair_type_id, :presence => true
Controller
#repairs_controller.rb
def create
#repair = Repair.new(params[:repair])
if #repair.save
redirect_to(repairs_path, :notice => 'Repair Created.')
else
#repair.repair_items.new(params[:repair][:repair_items_attributes]["0"].except(:_destroy))
render :new
end
end
Edit
I failed to mention that the association within the nested form is displayed using the select2 js plugin. This may be a factor as to why the Rails validations are not showing???

In you model add inverse_of
class Repair < ActiveRecord::base
has_many :repair_types, inverse_of: :repair
end
class RepairType < ActiveRecord::base
belongs_to :repair, inverse_of: repair_types
end

Related

Rails nested form with unknown columns

I'm creating an admin interface where the admin (of a company) can add custom fields to their employees.
Example:
Models:
Employee: Basic info like name, contact info, etc (has_many employee_field_values)
EmployeeFields: These are the dynamic ones the admin can add (every company has different needs, it could be anything), lets say favorite_food
EmployeeFieldValues: The actual values based on the fields above, say pizza (belongs_to both models above)
What's a smart way of adding the EmployeeFieldValues fields while editing an employee?
I'm trying something simple like this, but not sure if I like it
# Controller
#custom_fields = EmployeeFields.all
# View
<%= form_for(#employee) do |f| %>
<%= f.text_field :first_name %>
<% #custom_fields.each do |custom_field| %>
<%= custom_field.name %>
<%= text_field_tag "employee_field_values[#{custom_field.name}]" %>
<% end %>
<%= f.submit :save %>
<% end %>
And then when updating, params[:employee_field_values] gives this:
<ActionController::Parameters {"favorite_food"=>"pizza"}>
So, not sure if this is a good direction, also I'm not sure how to handle future edits to an employee's custom_fields if they change.
I think it will be better to use EmployeeField as nested model and EmployeeFieldValue for select field.
For example:
Models
class Employee < ActiveRecord::Base
validates :name, presence: true
has_many :employee_field_values
accepts_nested_attributes_for :employee_field_values, reject_if: ->(x) { x[:value].blank? }
end
class EmployeeFieldValue < ActiveRecord::Base
belongs_to :employee
belongs_to :employee_field
end
class EmployeeField < ActiveRecord::Base
has_many :employee_field_values, inverse_of: :employee_field, dependent: :destroy
validates :title, presence: true, uniqueness: true
end
Controller
class EmployeesController < ApplicationController
def new
#employee = Employee.new
#employee.employee_field_values.build
end
end
View
= simple_form_for #employee, url: '/' do |f|
= f.input :name
= f.simple_fields_for :employee_field_values do |ff|
= ff.input :value
= ff.input :employee_field_id, collection: EmployeeField.all.map{|x| [x.title, x.id]}
Also you need to make buttons for adding/removing :employee_field_value, and you can do it with gem cocoon for example
OR you can build all objects in controller(for each EmployeeField) and do without select box

Rails Mass Assignment

I have a site that keeps track of SAT tutoring sessions. The curriculum that the students learn is a collection of rules. I have a model for each tutoring session called "Sittings" and the rules model is called "Rules". I want the site admin to be able to enter a Sitting by date, and then use checkboxes to select which "rules" the student got wrong in that sitting. I'm a little confused as to how I can create the form to pull out specific rules without adding attributes to my Sitting model of rule1, rule2, etc. I'm using simple_form to create my forms.
My Sitting model:
class Sitting < ActiveRecord::Base
attr_accessible :date, :comment, :rule_id, :user_id
validates :date, presence: true
belongs_to :user
has_many :combos
has_many :rules, :through => :combos
end
My Rules model:
class Rule < ActiveRecord::Base
attr_accessible :name, :subject, :session_id, :hint_id, :question_id, :trigger_id
validates :name, presence: true
validates :subject, presence: true
has_many :questions
has_many :triggers
has_many :hints
has_many :combos
has_many :sittings, :through => :combos
end
My Combo model:
class Combo < ActiveRecord::Base
belongs_to :sitting
belongs_to :rule
end
Edit:
Here's what I have tried for the form. It does create the checkbox form, but my DB isn't updating the rule_id. (shows as nil when I create a Sitting)
form:
<%= simple_form_for(#sitting, html: { class: "form-horizontal"}) do |f| %>
<%= f.error_notification %>
<% Rule.all.each do |rule| %>
<%= check_box_tag "sitting[rule_ids][]", rule.id, #sitting.rule_ids.include?(rule.id) %> <%= rule.id %>
<% end %>
<div class="form-group">
<%= f.input :comment, as: :text, input_html: { rows: "2", :class => "form-control" }, label: "Comments:" %>
</div>
<div class="form-group">
<%= f.date_select :date, as: :date, label: "Taken Date:" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I updated my strong params to allow an array:
def create
#sitting = Sitting.new(sitting_params)
respond_to do |format|
if #sitting.save
format.html { redirect_to #sitting, notice: 'Sitting was successfully created.' }
format.json { render action: 'show', status: :created, location: #sitting }
else
format.html { render action: 'new' }
format.json { render json: #sitting.errors, status: :unprocessable_entity }
end
end
end
def sitting_params
params.require(:sitting).permit(:comment, :date, :user_id, :rule_id => [])
end
Am I missing something in order to update the Sitting.rule_id properly? I get the following error in my logs:
WARNING: Can't mass-assign protected attributes for Sitting: rule_ids
app/controllers/sittings_controller.rb:27:in `create'
Just to summarize what we got through over the chat.
Firstly, you do not need both attr_accessible and strong_params at the same time. I've posted another answer some time ago explaining how those two approaches differs from each other.
You're running rails 4, so you should take advantage of strong params and not use protected_attributes gem. In short, remove this gem from you Gemfile as well as all attr_accessible calls.
As Marian noticed, you have a typo in your strong params method, you need to permit rule_ids, not rule_id. rule_id column is obsolete, as sitting has_many :rules :through rather than sitting belongs_to :rule - most likely it is an artifact of old association code.
As soon as rule_ids are assigned within your model, it will create new join models in your join table, creating an association between given sitting and passed rules.

How do I save this nested form in Rails 4 with a has many through association?

The problem that I have here is that I have a nested form that won't save to the database and I'm suspecting it's because the proper attributes aren't being passed into the form prior to being saved. Right now I'm trying to pass these values through hidden fields but I'm thinking there's probably a more "Railsy" way to do this. This is the form that I have created to do this:
<%= form_for #topic do |f| %>
<%= render "shared/error_messages", object: f.object %>
<%= f.fields_for :subject do |s| %>
<%= s.label :name, "Subject" %>
<%= collection_select :subject, :id, Subject.all, :id, :name, {prompt:"Select a subject"} %>
<% end %>
<%= f.label :name, "Topic" %>
<%= f.text_field :name %>
<div class="text-center"><%= f.submit class: "button radius" %></div>
<% end %>
This form generates a params hash that looks like this:
{"utf8"=>"✓", "authenticity_token"=>"PdxVyZa3X7Sc6mjjQy1at/Ri7NpR4IPUzW09Fs8I710=", "subject"=>{"id"=>"5"}, "topic"=>{"name"=>"Ruby"}, "commit"=>"Create Topic", "action"=>"create", "controller"=>"topics"}
This my model for user.rb:
class User < ActiveRecord::Base
has_many :topics
has_many :subjects, through: :topics
end
In my subject.rb file:
class Subject < ActiveRecord::Base
has_many :topics
has_many :users, through: :topics, dependent: :destroy
validates :name, presence: true
end
In my topic.rb file:
class Topic < ActiveRecord::Base
belongs_to :subject
belongs_to :user
accepts_nested_attributes_for :subject
validates :name, presence: true
end
class TopicsController < ApplicationController
before_filter :require_login
def new
#topic = Topic.new
#topic.build_subject
end
def create
#topic = Topic.new(topic_params)
#topic.user_id = current_user.id
#topic.subject_id = params[:subject][:id]
if #topic.save
flash[:success] = "Success!"
render :new
else
flash[:error] = "Error!"
render :new
end
end
private
def topic_params
params.require(:topic).permit(:name,:subject_id,:user_id, subjects_attributes: [:name])
end
end
So I'm getting closer to having a successful form submission! I placed the method accepts_nested_attributes_for in the join model, which in this case is in topic.rb. I don't really know why this works but I'm thinking it allows Rails to properly set the ":user_id" and ":subject_id" compared to placing accepts_nested_attributes_for on a model containing the "has_many through" relationship. I saw it on this post btw: http://makandracards.com/makandra/1346-popular-mistakes-when-using-nested-forms
NOW, I still have a problem where the ":subject_id" isn't being properly saved into the database. Would I have to pass in a hidden field to do this or would I have to do something else like nest my routes?
Wow that took forever to figure this one out. Since I have a has_many through relationship and I'm trying to created a nested form involving one of these models the problem I was having was I was placing the "accepts_nested_attributes_for" in the wrong model I was placing it in the has_many through model in the subject.rb file when it should have been placed in the model responsible for the join between two tables.
Also I made a super idiotic mistake on this line when I was trying to save the ":subject_id". I was writing this: #topic.subject_id = params[:subject_id][:id] instead of something like this:
#topic.subject_id = params[:subject][:id]
It was a really dumb mistake (probably because I was copying a pasting code from another controller haha)
Anyways I hope others can learn from my mistake if they ever want to do a nested form on models with a "has_many through" relationship, in certain cases the "accepts_nested_attributes_for" method will go on the JOIN table and NOT on the model with the "has_many through" relationship

Rails: nested_form_for <column> can't be blank

I am trying to use the nested_form gem to create a dropdown that will show a list of chains and their locations. When the user clicks the submit button, the nested_form should create a new entry in a joiner table called users_locations. However, the form spits out an error as follows:
User locations user can't be blank
In other words, its complaining that the user_id is NULL and therefore it cannot create an entry in the table (because user_id is a required field in the user_locations table).
_form.html.erb (In the User view folder)
<div class="field" id="location_toggle">
<%= f.label "Location:", :class => "form_labels", required: true %>
<%= f.fields_for :user_locations do |location| %>
<%= location.grouped_collection_select :location_id, Chain.all, :locations, :name, :id, :name, include_blank: false %>
<% end %>
<%= f.link_to_add "Add a Location", :user_locations, :class => "btn btn-primary btn-small" %>
<br/>
</div>
user.rb (model)
belongs_to :chain
has_many :user_locations
has_many :locations, :through => :user_locations
...
accepts_nested_attributes_for :user_locations, :reject_if => :all_blank, :allow_destroy => true
validates_associated :user_locations, :message => ": you have duplicate entries."
...
attr_accessible :first_name, :last_name, ... , :user_locations_attributes
users_controller.rb
def create
#user = User.new(params[:user])
#user.save ? (redirect_to users_path, notice: 'User was successfully created.') : (render action: "new")
end
Log shows:
SELECT 1 AS one FROM "user_locations" WHERE ("user_locations"."user_id" IS NULL AND "user_locations"."location_id" = 1)
I'm assuming you're user_location validates :user_id, presence: true or something, right?
I had a look at this recently in response to this question, it seems that, when creating nested models, validations for all objects to be created are run before any of them are saved, and thus even though your model will get have user_id set when it is saved, it cannot have it when validated.
To get around this you may want to disable the validation on creation, for example with:
# user_locations.rb
validates :user_id, presence: true, on: :update
It looks like you're blending simple_form with the default Rails form helpers and getting some odd behavior. I haven't worked with simple_form, but my guess is that you can fix your problem by changing f.fields_for to f.simple_fields_for.
Here's an example that may help.

Rails 3 fail validation for parent model is validation of nested attributes fail

I have two models, User and House. They have one-to-one association.
class User < ActiveRecord::Base
attr_accessible :name, :house_attributes
has_one :house, :dependent => :destroy
validates :name, :presence => true
accepts_nested_attributes_for :house, allow_destroy: true, :reject_if => lambda { |a| a['desc'].blank? }
end
class House < ActiveRecord::Base
attr_accessible :desc, :price
belongs_to :user
validates :desc, :presence => true
end
Now I created a nested form inside of User new view like this:
<%= simple_form_for(#user) do |f| %>
<%= f.input :name %>
<%= f_builder.simple_fields_for :house, #house do |h| %>
<%= h.input :price %>
<%= h.input :desc %>
<% end %>
<%= f.button :submit %>
<% end %>
And the new controller is like this
def new
#user = User.new
#house = #user.build_house
respond_to do |format|
format.html # new.html.erb
end
end
I want to always create a house at the same time that user is created. So if house fails validation, it should not create user. (Now the house model has only one validation, which is :desc field needs to be present.)
The code in models, only guarantee house is not created, if :desc is blank. But it will still create the user.
I tried to add custom validation inside of User model, but can not find a way to access :desc attribute (failed to call self.desc) or :house_attributes (self.house_attributes) inside of User model. I really don't know where does rails store these house attributes before a house is created.
I hope you guys can help me figure out a nice and clear way to
1) Be able to validate and show error message for :desc attribute. Right now errors message will only show up for :name field of User model.
2) Do not save user if validation for either user or house is failed.
3) If any of validation failed, render :new
Thanks
You should add validates_associated :house to your User model. This will run the House validations and ensure they succeed before considering the user to be valid.

Resources