I have 2 nested forms such that Estates has Revenue and Estates has Expense, and current form signature looks like for expense "<%= form_for [#estate,#expense] do |f| %>" and for revenue "<%= form_for [#estate,#revenue] do |f| %>". My routes for these are
resources :estates do
resources :revenues
resources :expenses
resources :net_profits
end
Now can I open revenue and expense form on the same page on click of 1 new button and 1 submit button for both these forms ?
First of all you need into your model Estate the following:
accepts_nested_attributes_for :revenue
accepts_nested_attributes_for :expense
Then you can just generate it with a form_for like this:
= form_for(#estate, :url => {:action=>'update'}) do |f|
= f.fields_for :revenue do |r|
# revenue fields here, for example:
= r.text_field :name
= f.fields_for :expense do |e|
# expense fields here, for example:
= e.text_field :name
This is an example with HAML markup and for an update of the object. You could however modify it for object generation (create).
Related
I am using a link from an index page that has a group of nested records (row) that I need to update all at once. The link goes to an edit action that I need to make update the attributes of nested records (prisms).
I tried using the simple_form gem methods for nested models. It gives me a field for all of the objects, when I only want one field to enter a value to them all. The builder from that looks usable, but I don't know how to use it to update the fields. Either way, the form isn't right.
I have tried every variation of form_for and fields_for I could find on Google to develop the edit form. It looks like I'm the only one on Earth trying to solve this problem.
This is how I have my routes set up:
resources :gardens, shallow: true do
resources :prisms
resources :rows
Here is how my garden model is now:
class Garden < ApplicationRecord
mount_uploader :picture, ImageUploader
belongs_to :user
has_one :photo
has_many :rows, :dependent => :destroy
has_many :prisms
geocoded_by :address
after_validation :geocode
after_commit :populate_garden!
def id
self[:id]
end
def populate_garden!
# row 0
(0..length-1).each do |i|
Row.create(row_num: i, garden_id: id)
end
end
end
The garden model creates my rows through the populate_garden! method.
Here is the row model:
class Row < ApplicationRecord
belongs_to :garden
has_many :prisms, :dependent => :destroy
accepts_nested_attributes_for :prisms
after_commit :populate_rows
def id
self[:id]
end
def populate_rows
# row 0
(0..garden.width-1).each do |i|
Prism.create(:row_id => self.id, :row_num => self.row_num, :col_num => i, :garden_id => self.garden_id)
end
end
end
The row model creates prisms in the populate_rows method.
Here is the prism model:
class Prism < ApplicationRecord
belongs_to :row
belongs_to :garden
include RankedModel
ranks :column_order
end
Here is the table from my index.html.erb that I click to open the edit action.
<table>
<% #rows.each_with_index do |gardenrow, index| %>
<% #rows.select { | row | row.row_num == index}.each do |row| %>
<td class="prism-cols">
<%= link_to 'Edit Row', edit_row_path(row), class:"dark-link" %>
<br /><i class="fas fa-arrow-down"></i>
</td>
<% end %>
<% end %>
</table>
The row passes nicely into the edit action, and I currently have this incorrect form:
<h1>The row brought into this form page is: </h1>
<%= #row.inspect %>
<div class="container">
<%= simple_form_for #row do |m| %>
<%= m.simple_fields_for :prisms do |p| %>
<%= p.input :crop_name %>
<% end %>
<%= m.button :submit %>
<% end %>
</div>
The rows_controller update method looks like this:
def update
#row = Row.find(params[:row_id])
#row.prisms.build
redirect_to root_path
end
I need one form field for crop_name that will change all of the prisms in the selected row with a single submit. I don't have any problems updating one prism at a time through an edit action on the prism. The difficulty I'm having is working through the nesting of prisms inside of a specific row.
With the help of my mentor below I was able to develop a form that works with the controller to make this work. Here is the updated code for later use with this type of problem.
Here is the form data:
<%= form_tag({controller: "rows", action: "update"}, method: "patch") %>
<%= label_tag(:crop_name, "Crop Name") %>
<%= text_field_tag(:crop_name) %>
<%= hidden_field_tag(:row_id, #row.id) %>
<%= submit_tag("submit") %>
Here is the controller update method:
def update
#row = Row.find(params[:id])
#garden = Garden.find_by_id(:garden_id)
#row.prisms.each do |p|
p.crop_name = params[:crop_name]
p.save!
end
redirect_to :controller => 'gardens', :action => 'show', id: #row.garden_id
end
Thanks for the help. I don't think I could have figured this out from the documentation alone.
If I'm understanding correctly, I think simple_form may be limiting you. A basic ruby form may do what you want. I'm not 100% sure what the best way is to do a simple_form on nested fields but this stackoverflow answer may be able to help more.
Using a basic ruby form
You want a form that has one field. When submitted, it will take the value from the submitted form and update that field for all prisms of that row. I would recommend digging more into the basics of ruby forms for this kind of scenario and then do something like this.
// html.erb
<%= form_tag({controller: "rows", action: "update_prism_crop_name"}, method: "post", class: "nifty_form") %>
<%= label_tag(:crop_name, "Crop name") %>
<%= text_field_tag(:crop_name) %>
<%= hidden_field_tag(:row_id, #row.id) %>
<%= submit_tag("Submit") %>
<% end %>
// rows_controller
def update_prism_crop_name
#row = Row.find(params[:row_id])
#row.prisms.each do |prism|
prism.crop_name = params[:crop_name]
prism.save!
end
# other redirect stuff
end
The form_tag explicitly calls out an action but I have to imagine that you'll need to build a route for this custom action as well.
I haven't tested any of this and I'm a bit rusty in rails but I believe something like this would work.
7 Patterns to Refactor Fat ActiveRecord Models - here is a great article about different refactoring approaches using PORO. Under the 3rd caption there is a Form Object pattern, which I really liked and already implemented in one of the projects. There is only an example using one nested resource, but I would like to implement this pattern for multiple nested resources. Maybe someone here had already dealt with this? I don't necessarily need any code examples, just the basic idea would be fine.
Update
Consider this example. I have two models.
class Company
has_many :users
accepts_nested_attributes_for :users
end
class User
belongs_to :company
end
In case of one nested user for company using Form Object Pattern I would write the following:
<%= form_for #company_form do |f| %>
<%= f.text_field :name %>
<%= f.text_field :user_name %>
<%= f.submit %>
<% end %>
Form Object
class CompanyForm
include Virtus
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_accessor :company, :user
def user
#user ||= company.users.build
end
def company
#company ||= Company.new
end
def submit(params={})
company.name = params[:name]
user.name = params[:user_name]
persist!
end
private
def persist!
company.save!
user.save!
end
end
But what if I have a form, where a company with multiple users can be created. The usual approach is to write it like this, using nested_form:
<%= nested_form_for #company do |f| %>
<%= f.text_field :name %>
<%= fields_for :users, do |user_form| %>
<%= user.form.text_field :name %>
<% end %>
<%= f.link_to_add "Add a user", :users %>
<%= f.submit %>
<% end %>
What I am asking is how do I implement that Form Object Pattern in this case?
the rails fields_for helper checks for a method in this format: #{association_name}_attributes=
so, if you add this method to CompanyForm:
def users_attributes=(users_attributes)
# manipulate attributes as desired...
#company.users_attributes= users_attributes
end
def users
company.users
end
the fields_for generators will generate the nested users fields for a CompanyForm as if it were a Company. the above could be rewritten as a delegation since nothing is happening in the methods:
delegate :users, :users_attributes=, :to => :company, :prefix => false, :allow_nil => false
I have an account dashboard that lists the offices and for each office the jobs available.
Hierarchy:
Companies (1 user has 1 company, I access if from the user profile)
Offices (each company can have multiple offices)
Jobs (each office can have multiple jobs)
Models:
class Company < ActiveRecord::Base
has_many :offices, :dependent => :destroy
has_many :jobs, :through => :offices
class Office < ActiveRecord::Base
belongs_to :company
has_many :jobs, :dependent => :destroy
class Job < ActiveRecord::Base
belongs_to :office
For each job I have an edit link
E.g. for job id 10 (job is a local variable from iteration on office jobs)
= link_to edit_job_path(I18n.locale, job)
-> localhost:3000/de/jobs/10/edit
When I click on the edit link, I go to the edit page. So far so good, but the form looks like that:
<form accept-charset="UTF-8" action="/10/jobs/10" class="edit_job" enctype="multipart/form-data" id="edit_job_10" method="post">
Notice, that my locale (de in this example) disappeared and has the job id instead!
My routes.rb
scope "/:locale" do
resources :companies
resources :offices do
resources :jobs
end
resources :jobs
end
There are two jobs mentioned, I probably can do without but it's an easy way for me to either mention directly the job url to view or add office variable in the create new job link and use :office_id (in my dashboard controller: link_to new_office_job_path(I18n.locale, office) then in my form for new jobs: = f.hidden_field :office_id)
But even if I remove the resources :jobs in the :offices. The locale is still replaced by the job id in the edit form.
Note that I can edit the job properly, but because the locale is changed, the localisation text are all wrong after my redirect.
Any idea how to fix that?
------ Additional data requested -----------
= form_for(#job) do |f|
.field
= f.label :name, t(:job_title)
= f.text_field :name
.field
= f.label :url, t(:job_url)
= f.text_field :url
.field
= f.hidden_field :office_id
.field
= f.label :pdf, t(:job_upload_pdf)
= f.file_field :pdf
.field
= f.label :tag_list, t(:job_tags)
= f.text_field :tag_list
.actions
= f.submit "Submit", :class => "btn btn-primary"
------ Additional information -----------
BTW: this work around works, I get /de/jobs/10 but I'd like to understand why the locale gets corrupted if I use the default form_for.
= form_for #job, :url => job_path(I18n.locale, #job) do |f|
You can handle the locale using a Routing Filter, I've tried it in my project, you don't have to worry about locale in routes, the filter will handle it for you.
# in config/routes.rb
Rails.application.routes.draw do
filter :locale
end
I hope this will solve your problem.
The form_for line builds the path, when building the path you also need to specify the locale. To do this cleanly (without specifying the url explicitly, which is also possible), write it as follows:
= form_for [I18n.locale, #job] do |f|
and that should render the correct path.
As specified in the documentation it will use the array to build the correct path (this works for namespaced and nested routes, so I am guessing it will also work for your locale).
An alternative is to specify the path explicitly, using the :url option.
HTH.
I have tried to get this working, looked at multiple tutorials, questions on here tried different things for about a week now and I can't get the view to work correctly.
I have teams of users. A team has_many users and a user belongs_to a team (one team at a time). I know the association works because I got it working using the console (with some help there). I'm not sure how to get it working in the view. Below is the code, please let me know if more is needed.
What am I missing?
_join_team_button
<%= form_for(#user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Team Show Page
<%= render 'shared/join_team_button %>
Teams Controller
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
Users Controller
def show
#user = User.find(params[:id])
#teams = #user.team
end
I tried to put a complete demonstration of what you are looking for. Let me know if it fits for you.
#FILE: models/team.rb
class Team < AR::Base
has_many :users
end
#FILE: models/user.rb
class User < AR::Base
belongs_to :team
end
#FILE: config/routes.rb
#Here you are defining "users" as a nested resource of "teams"
resources :teams do
resources :users do
member do
put :join
end
end
end
#if you run "rake routes" it will show you the following line along with others
join_team_user PUT /teams/:team_id/users/:id/join(.:format) users#join
#FILE: controllers/team_controller.rb
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = current_user.users.build if signed_in?
end
#FILE: views/teams/show.html.erb
<% if(#user) %>
<%= form_for #user, :url => join_team_user_path(#team, #user) do |f| %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
#You dont really need a form for this. You can simply use `link_to` like below
<%= link_to 'Join', join_team_user_path(#team, #user), method: :put %>
#FILE: controllers/users_controller.rb
def join
# params[:id] => is the user_id
#user = User.find(params[:id])
# params[:team_id] => is the team_id
#team = Team.find(params[:team_id])
# Now make the relationship between user and team here.
#user.update_attribute(:team, #team)
end
Update:Based on your comment
Q: Do I create a new user's resource and nest that or do I nest the already establishes user's resource?
Ans: Based on your requirements any resource can be defined both independently or nestedly. But yes you can control that which method will be available in which way. Like in your case, you can allow only join method when your user is nested under team resource.
resources :users, :only=>:join do
member do
put :join
end
end
resource :users
run rake routes with and without :only=>:join option and see differences in available routes.
Q: Will that affect other things?
Ans: If you strictly define your routes following above example, it should not affect other things. You should confirm all the available routes to your application by rake routes.
Q: Should I put my current routes.rb file up there?
Ans: Assuming your current routes.rb will be modified in the above way. Could I answer the question?
Q: Confused about the comments controller?
Ans: Im extreamely sorry. Yes it must be users_controller.rb as the rake routes command is showing. Result of copy and paste from my own example code :P
Q: what should I put there? the build method
Ans: In your case both the user and team is already exists in your database. All you need to do is just setup a relationship. So you can just use update_attribute option. Ive changed the join method. Please check. But yes if want to create new entries you might need build methods.
Sorry for the late reply :)
I figured it out. Still not perfect, but it gets the association working. The team, and the user were already created, I just needed to establish the association, so the build method would not have worked. Here's what I have:
View:
<%= form_for(#user) do |f| %>
<%= f.hidden_field :team_id, :value => #team.id %>
<%= f.submit "Join Team", class: "btn btn-large btn-primary" %>
<% end %>
Teams Controller:
def show
#team = Team.find(params[:id])
#team_members = #team.users
#user = User.find(params[:id]) if signed_in?
end
How can I get a text area in a Rails form to pre-populate with the attribute of a model?
For instance, with the following:
= form_for #posts do |f|
.field
= f.label :company_name
= f.text_field :company_name
How could I get company_name to pre-populate with user.company_name?
You'll need the form_for #user so it could access the user model's attribute company_name.
= form_for #user do |f|