I have a has_many belongs_to relationship between Job and Address
class Job < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :job
end
Inside the Job form I inserted the proper fields for text:
<%= f.fields_for :addresses do |address| %>
<%= address.label :label, "Label" %>
<%= address.text_field :label %>
<%= address.label :addy, "Address" %>
<%= address.text_field :addy %>
<%= address.label :apt, "Apt/Suite/etc" %>
<%= address.text_field :apt %>
<%= address.label :city, "City" %>
<%= address.text_field :city %>
<%= address.label :state, "State" %>
<%= address.text_field :state %>
<%= address.label :zip, "Zip code" %>
<%= address.text_field :zip %>
<% end %>
But the fields don't appear. Am I missing something?
EDIT: Added the create action from the Jobs controller
def create
#job = Job.new(job_params)
#job.addresses.build
#job.user_id = current_user.id
if #job.save
render 'show'
else
render 'new'
end
end
What does your Controller look like? Are you building the addresses for the Job?
For example, you need to do something like this in your new action in your controller:
def new
#job = Job.new
#job.addresses.build
end
I hope this helps.
Related
I am trying to implement a form that has associated videos when I create a new tutorial. The issue is that my form is not rendering whenever I visit the page. Could anyone help, please?
This is myapp/views/admin/tutorials/new.html.erb
<h2>New Tutorial</h2>
<%= form_for [:admin, #tutorial] do |f| %>
<%= f.label :title %>
<%= f.text_field :title, class: "block col-4 field" %>
<%= f.label :description %>
<%= f.text_area :description, class: "block col-4 field" %>
<%= f.label :thumbnail %>
<%= f.text_field :thumbnail, class: "block col-4 field" %>
<h3>Videos</h3>
<%= f.fields_for :videos do |builder| %>
<%= builder.label :youtube_url %>
<%= builder.text_field :youtube_url, class: "block col-4 field" %>
<%= builder.label :video_id %>
<%= builder.text_field :video_id, class: "block col-4 field" %>
<% end %>
<%= f.submit "Save", class: "mt2 btn btn-primary mb1 bg-teal" %>
<% end %>
This is my Video model
class Video < ApplicationRecord
has_many :user_videos
has_many :users, through: :user_videos
belongs_to :tutorial
validates_presence_of :position
end
And my Tutorial model
class Tutorial < ApplicationRecord
has_many :videos, -> { order(position: :ASC) }, dependent: :destroy
acts_as_taggable_on :tags, :tag_list
accepts_nested_attributes_for :videos
scope :without_classroom, -> { where(classroom: false) }
end
My controller:
class Admin::TutorialsController < Admin::BaseController
def new
#tutorial = Tutorial.new
end
def create
#tutorial = Tutorial.new(tutorial_params)
if #tutorial.save
flash[:success] = "Successfully created tutorial!"
redirect_to tutorial_path(#tutorial)
else
render :new
end
end
def edit
#tutorial = Tutorial.find(params[:id])
end
def update
tutorial = Tutorial.find(params[:id])
if tutorial.update(tutorial_params)
flash[:success] = "#{tutorial.title} tagged!"
end
redirect_to edit_admin_tutorial_path(tutorial)
end
private
def tutorial_params
params.require(:tutorial).permit(:tag_list, :title, :description, :thumbnail)
end
end
The reason nothing's rendered is that the videos association is empty. For example if you add #tutorial.videos.build in the new controller action, then you will get one set of the fields, and if you have multiple videos in the association (whether persisted or not) you would get one set of the fields per video.
I'm trying to come up with a contact form that creates a contact record and potentially multiple location records, if multiple locations are checked in a list of checkboxes. I thought of having all location records created and then destroyed, if they aren't checked. I don't think that's optimal though.
I'm using many to many relationships in the models.
This is what they look like at the moment:
contact.rb
class Contact < ApplicationRecord
has_many :contact_locations, dependent: :destroy
has_many :locations, through: :contact_locations
accepts_nested_attributes_for :contact_locations, allow_destroy: true, reject_if: :empty_location?
private
def empty_location?(att)
att['location_id'].blank?
end
end
location.rb
class Location < ApplicationRecord
has_many :locations, dependent: :destroy
has_many :contacts, :through => :contact_locations
has_many :contact_locations
end
contact_location.rb
class ContactLocation < ApplicationRecord
belongs_to :location
belongs_to :contact
end
contacts_controller.rb
def new
#contact = Contact.new
#locations = Location.all
4.times {#contact.contact_locations.new}
end
private
def contact_params
params.require(:contact).permit(:name, :phone, ..., contact_locations_attributes: [:location_ids])
end
new.html.rb
<%= form_with model: #contact do |f| %>
...
<%= #locations.each do |location| %>
<%= f.fields_for :contact_locations do |l| %>
<%= l.check_box :location_id, {}, location.id, nil %><%= l.label location.name %>
<% end %>
<% end %>
...
<% end %>
Does anyone how to make it work properly?
I'm working on Ruby 2.5.1 and Rails 5.2.1.
Thanks a lot.
I think your solution is the form objects pattern.
You can have something like this:
<%= form_for #user do |f| %>
<%= f.email_field :email %>
<%= f.fields_for #user.build_location do |g| %>
<%= g.text_field :country %>
<% end %>
<% end%>
And convert it in something more readable that permits you to instance the locations inside the registration object, checking the value of the checkboxes.
<%= form_for #registration do |f| %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.input :password %>
<%= f.text_field :password %>
<%= f.input :country %>
<%= f.text_field :country %>
<%= f.input :city %>
<%= f.text_field :city %>
<%= f.button :submit, 'Create account' %>
<% end %>
Here you will find how to apply the pattern: https://revs.runtime-revolution.com/saving-multiple-models-with-form-objects-and-transactions-2c26f37f7b9a
I ended up making it work with Kirti's suggestion on the following question:
Rails Nested attributes with check_box loop
It turns out I needed to make a small adjustment in my form's fields_for tag.
Thanks a lot the help!
I am running a Rails 5.1 app with the following information:
Models
class Company < ApplicationRecord
has_many :complaints
accepts_nested_attributes_for :complaints
validates :name, presence: true
end
class Complaint < ApplicationRecord
belongs_to :company
validates :username, :priority, presence: true
end
Controller
class ComplaintController < ApplicationController
def new
#company = Company.new
#company.complaints.build
end
def create
#company = Company.new(company_params)
respond_to do |format|
if #company.save
format.html { redirect_to complaint_url }
else
format.html { render :new }
end
end
end
private
def company_params
params.require(:company).permit(:name, complaints_attributes: [:username, :priority])
end
Form in view
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
If I have just one input field for the complaint_attributes part of the form (in other words just one field for username and one field for priority as shown above), this works just fine.
However, if I want to have multiple fields for username/priority in the form, so that I can submit multiple username/priority combinations in a single submission, I find that submitting the form will only save the last username/priority values from the form. Example of this view would be:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<div>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
</div>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
I noticed that when submitting the form, I get a hash like this (for submitting single complaint):
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}}}, "commit"=>"Submit"}
Is there any way to modify the params to make it similar to this and have it saved to the DB?:
{"utf8"=>"✓", "authenticity_token"=>"...", "company"=>{"name"=>"Test", "complaints_attributes"=>{"0"=>{"username"=>"test_person", "priority"=>"1"}"1"=>{"username"=>"test_person", "priority"=>"2"}}}, "commit"=>"Submit"}
Or if not the above, what would be the best way to have the username/priority values saved if using multiple fields for them in a single form?
EDIT: I should point out that I can dynamically add the username/priority field groups as needed, so I don't want to be restricted to a set number.
the second block will override the first fields... you should instead build many complaints in the controller:
def new
#company = Company.new
3.times { #company.complaints.build }
end
and then with the following form it should generate to inputs according to the number of complaints you have built:
<%= form_for #company do |f| %>
<%= f.label :name, "Company" %>
<%= f.text_field :name, type: "text" %>
<%= f.fields_for :complaints do |complaint| %>
<%= complaint.label :username, "Username" %>
<%= complaint.text_field :username %>
<%= complaint.label :priority, "Priority" %>
<%= complaint.text_field :priority %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
Nested attributes of join model won't be saved. relation id's seems to be missing. The following error messages are added when the fields get validated:
* Assigned projects user can't be blank
* Assigned projects project can't be blank
The submitted params look like this ( <%= debug(params) %> )
--- !map:ActionController::Parameters
utf8: "\xE2\x9C\x93"
authenticity_token: HrF1NHrKNTdMMFwOvbYFjhJE1ltlKbuz2nsfBYYBswg=
project: !map:ActionController::Parameters
name: Peter Pan
assigned_projects_attributes: !map:ActiveSupport::HashWithIndifferentAccess
"0": !map:ActiveSupport::HashWithIndifferentAccess
position: Group Leader
currency: " Neverland Dollars"
commit: Add Project
action: create
controller: projects
I have 3 models, as followed:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class AssignedProject < ActiveRecord::Base
belongs_to :user, class_name: "User"
belongs_to :project, class_name: "Project"
attr_accessible :project_id, :user_id, :position, :project_attributes
accepts_nested_attributes_for :project
validates :user_id, presence: true
validates :project_id, presence: true
validates :position, presence: true
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
attr_accessible :name, :creator_id, :currency :assigned_projects_attributes
accepts_nested_attributes_for :assigned_projects
validates :name, presence: true, length: { minimum: 3, maximum: 100 }
validates :currency, presence: true, length: { minimum: 1, maximum: 5 }
validates :creator_id, presence: true
end
So each User can create a Project. He can add any User to the Project through the join model.
Each Project belongs to a User resp. Creator and has_many user through assigned_projects
I want to give each user of a project a "position", which should be saved in the join model: assigned_project :position
the Project controller looks like that:
class ProjectsController < ApplicationController
def new
#project = Project.new
#project.assigned_projects.build(user_id: current_user)
end
def create
#project = current_user.assigned_projects.build.build_project(params[:project])
#project.creator = current_user
if #project.save
redirect_to current_user
else
render 'new'
end
end
end
and the project/new.html.erb form looks like that:
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>
UPDATE: current controller & view
def create
#project = Project.new(params[:project])
if #project.save
redirect_to current_user
else
render 'new'
end
end
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :creator_id, value: current_user.id %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>
View:
I think you need to pass the objects collection #project.assigned_projects you built in the new action to the fields_for:
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
Controller:
If i understood the first line in the create action i think you try to re-build the project assigned_projects in-order to stamp the creator attribute !!
Instead you could remove this line and put a hidden field in the nested for, something like:
<%= ff.hidden_field :creator, current_user %>
So your controller looks pretty basic now:
def create
#project = Project.new(params[:prject])
if #project.save #nested objects will be saved automatically
redirect_to current_user
else
render 'new'
end
end
What does the build_project method do?
I think in your controller you should just have build, not build.build_project, so like this:
#project = current_user.assigned_projects.build(params[:project])
of if build_project is a method used to create the params then
#project = current_user.assigned_projects.build(project_params)
in the case of rails 4 you would need something like this:
def project_params
params.require(:project).permit(:your_params)
end
In the case of rails 3 I think you need to add
attr_accessible :param1, :param2
in the project model for the parameters you want to set.
Problem is solved by removing the validations for project_id and user_id in the join table "AssignedProject"
So the join Model looks like that:
# Join Model AssignedProject
class AssignedProject < ActiveRecord::Base
belongs_to :user#, class_name: "User"
belongs_to :project#, class_name: "Project"
attr_accessible :project_id, :user_id, :position, :project, :project_attributes
accepts_nested_attributes_for :project
validates :position, presence: { message: " can't be blank." }
end
The New and Create methods look like that:
# Projects Controller
class ProjectsController < ApplicationController
def new
#project = Project.new
#project.assigned_projects.build(user_id: current_user)
end
def create
#project = Project.new(params[:project])
if #project.save
#project.assigned_projects.create(user_id: current_user)
redirect_to current_user
else
render 'new'
end
end
end
And the form in the view for the new method looks like that:
<%= form_for( #project ) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.hidden_field :creator_id, value: current_user.id %>
<%= f.fields_for :assigned_projects, #project.assigned_projects do |ff| %>
<%= ff.hidden_field :project_id, value: #project %>
<%= ff.hidden_field :user_id, value: current_user.id %>
<%= ff.label :position %>
<%= ff.text_field :position%>
<% end %>
<%= f.label :currency %>
<%= f.text_field :currency %>
<%= f.submit "Add Project", class: "" %>
<% end %>
The nested form in the view just won't render, unless I remove the f attribute, in which case the submit button will not work. I have two models, job and employer. I've been following the railscast here
job.rb
attr_accessible :title, :location, :employers_attributes,
belongs_to :employers
accepts_nested_attributes_for :employers
employer.rb
attr_accessible :companyname, :url
has_many :jobs
jobs_controller.rb
def new
#job = Job.new
#employer = Employer.new
end
_form.html
<%= form_for(#job) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.fields_for :employers do |builder| %>
<%= builder.label :companyname, "Company Name" %>
<%= builder.text_field :companyname %>
<%= builder.label :url, "Web Address" %>
<%= builder.text_field :url %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any input would be brilliant - thanks
This happens because your job has no employers.
Change your code to this:
def new
#job = Job.new
#job.employer = #job.build_employer
end
In your job.rb change:
attr_accessible :title, :location, :employer_attributes,
belongs_to :employer
accepts_nested_attributes_for :employer
This line:
belongs_to :employers
Should be singulars:
belongs_to :employer
With this association you not need nested form you can use select for pick employer for each job.
But if you need many employers for each job and each job can have many employers see this screencast