Conditional nested forms with simple_form - ruby-on-rails

I have 2 models, clients and client_prices, I would like to have 2 client prices nested forms sections in the client's show page. 1 nested form would be for non-custom prices and another for custom prices. The non-custom (custom == false) prices would only have the "custom" attribute available to edit. The "Custom Prices" would have all of the attributes available to edit.
I've tried a few different approaches, but I don't know where to put the conditional logic to make this work correctly. I'm using simple_form to generate the forms, but I'm not married to it.
Controllers
class Client < ActiveRecord::Base
attr_accessible :name, :client_prices_attributes
has_many :client_prices
accepts_nested_attributes_for :client_prices, :allow_destroy => true
end
class ClientPrice < ActiveRecord::Base
attr_accessible :client_id, :price, :visit_type, :id, :custom
belongs_to :client
belongs_to :default_price
end
Client Show page
<%= simple_nested_form_for #client do |f| %>
<%= f.fields_for :client_prices do |def_price_form| %>
non-custom prices form here
<%end%>
<%= f.fields_for :client_prices do |def_price_form| %>
custom prices form here
<%end%>
<%end%>

Try the following:
<%= simple_nested_form_for #client do |f| %>
<%= f.fields_for :client_prices do |def_price_form| %>
<%= if def_price_form.object.custom %>
Here you fields for custom form
<% end %>
<% end %>
<% end %>

Related

Simple Form check box for join table relationship

I can't figure this out for the life of me but here are my models:
class User < ApplicationRecord
has_many :user_stores
has_many :stores, through: :user_stores
end
class UserStore < ApplicationRecord
belongs_to :user
belongs_to :store
end
class Store < ApplicationRecord
has_many :user_stores
has_many :users, through: :user_stores
end
So I have a join table, I'm trying to make a form, which would have selected checkboxes next to the store names that the user has selected (this information would come from the join table relationship) and open checkboxes for the remaining stores (coming from the Store model). How do I show that in the view/make it work in the controller as well. Would I use collections instead? ( I am using Devise and Simple Form gem )
This is what I have so far:
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.fields_for :stores, #user.stores do |s| %>
# not sure if this is the right way or not
<% end %>
<%= f.button :submit %>
<% end %>
Store Controller:
class StoresController < ApplicationController
...
def new
#user = current_user
#stores = Store.all
# #user.stores => shows user's stores (from join table)
end
end
When you set up a one or many to many relationship in rails the model gets a _ids setter:
User.find(1).store_ids = [1,2,3]
This would for example setup a relation between user 1 and the stores with ids 1,2 and 3.
The built in Rails collection form helpers make use of this:
<%= form_for(#user) do |f| %>
<% f.collection_check_boxes(:store_ids, Store.all, :id, :name) %>
<% end %>
This creates a list of checkboxes for each store - if an association exists it will already be checked. Note that we are not using fields_for since it is not a nested input.
SimpleForm has association helpers which add even more sugar.
<h1>Add Favorite Stores</h1>
<%= simple_form_for(#user, html: { class: 'form-horizontal' }) do |f| %>
<%= f.association :stores, as: :check_boxes %>
<%= f.button :submit %>
<% end %>

Rails: How do I submit multiple objects into strong params?

I am making a goal tracking app. Right now outcome, purpose, action, priority, resources, and direction are all things which are part of Outcome in the database. However, I want to make purpose and action their own model objects. What I am confused about is how do I submit Outcome, Purpose, and Action, which will be 3 separate model objects, in a single HTTP request?
Should I just use multiple strong params in my controller?
app/view/outcomes/new.html.erb
You need to have model associations of outcomes with purpose and action.
Then you will need to create nested form. So that outform form can wrap purpose and action model attributes.
As you want to have different models for actions and purposes, I'm assuming outcome can has_many purposes and has_many actions. As per this type of association, below is the code you should have.
Your form will become something like:
<%= form_for #outcome do |f| %>
<%= f.label :outcome, "Outcome" %>
<%= f.text_area :outcome %>
<%= f.fields_for :purpose, #outcome.purpose.build do |p| %>
<%= p.text_area :desc, label: "Purpose" %>
<% end %>
<%= f.fields_for :action, #outcome.action.build do |p| %>
<%= p.text_area :desc, label: "Action" %>
<% end %>
<%= f.submit "submit" %>
<% end %>
Models:
# outcome.rb
has_many :purposes, :dependent => :destroy
has_many :actions, :dependent => :destroy
accepts_nested_attributes_of :purposes, :actions
-----------------------------------------
# purpose.rb
belongs_to :outcome
-----------------------------------------
# action.rb
belongs_to :outcome
Controller:
# outcomes_controller.rb
def outcome_params
params.require(:outcome).permit(:outcome, purpose_attributes:[:desc], action_attributes: [:desc])
end
SUGGESTION: You should rename your action model name to avoid unwanted conflicts with rails keyword action.
This may help you
Nestd Attributes
If the objects are associated (as below), you'll be best using the accepts_nested_attributes_for method:
#app/models/outcome.rb
Class Outcome < ActiveRecord::Base
has_many :purposes
has_many :actions
accepts_nested_attributes_for :purposes, :actions
end
#app/models/purpose.rb
Class Purpose < ActiveRecord::Base
belongs_to :outcome
end
#app/models/action.rb
Class Action < ActiveRecord::Base
belongs_to :outcome
end
accepts_nested_attributes_for means you'll be able to send the associated objects through the Outcome model - meaning you can send them all in a single HTTP request
You have to remember the way Rails is set up (MVC pattern), meaning if you send a single request; any further model objects you have will be able to be stored too.
Here's how you can set it up:
#app/controllers/outcomes_controller.rb
Class OutcomesController < ApplicationController
def new
#outcome = Outcome.new
#outcome.purposes.build
#outcoe.actions.build
end
def create
#outcome = Outcome.new(outcome_params)
#outcome.save
end
private
def outcome_params
params.require(:outcome).permit(:outcome, purpose_attributes:[:purpose], action_attributes: [:action])
end
end
Which will give you the ability to use this form:
#app/views/outcomes/new.html.erb
<%= form_for #outcome do |f| %>
<%= f.label :outcome %>
<%= f.text_area :outcome %>
<%= f.fields_for :purposes do |p| %>
<%= p.text_area :purpose %>
<% end %>
<%= f.fields_for :actions do |a| %>
<%= a.text_area :action %>
<% end %>
<%= f.submit %>
<% end %>
--
Recommendation
From the looks of it, I'd recommend you'll be able to keep all of these details in a single model - storing in multiple models seems overkill

Text_field not showing up

I'm building an open-source spreadsheet application. So far, its a rails application that has 4 models: table, row, column, and item.
Here is what they look like:
Table.rb:
has_many :columns
has_many :rows
accepts_nested_attributes_for :columns
Column.rb
belongs_to :table
has_many :items
Row.rb
belongs_to :table
has_many :items
accepts_nested_attributes_for :items
Item.rb
belongs_to :row
On the page where you add a new row. You should be presented with something like this:
So the columns are already set, so now you are adding new items which when inserted should include the row_id, column_id, and value for each item.
So far my form looks like this:
<%= nested_form_for [#table, #row] do |f| %>
<% #columns.each do |column| %>
<%= column.name %> <br>
<%= f.fields_for :items do |item_form| %>
<%= item_form.text_field :value %>
<% end %>
<% end%>
<%= f.submit %>
<% end %>
I'm using the nested_form for the nested forms. But so far, I can't get a textbox for the item's value to show up. Also, is this the best way to get what I want (like the picture), or is there a cleaner way?
Thanks for all help!
You'll need to build at least 1 item for fields_for to render anything. Try doing this in your new controller action.
def new
...
#row.items.build
end

Nested model form with mutliple has_many/belongs_to associations

I have three models:
class Rate < ActiveRecord::Base
attr_accessible :user_id, :car_id, :rate
belongs_to :user
belongs_to :car
end
class User < ActiveRecord::Base
attr_accessible :name
has_many :rates
accepts_nested_attributes_for :rates
end
class Car < ActiveRecord::Base
attr_accessible :name
has_many :rates
accepts_nested_attributes_for :rates
end
And one controller:
class UsersController < ResourceController
def new
# Assume user is loaded
#user.rates.build
end
end
I'm trying to build a nested form that will associate a list of users/cars and their associated rates.
Something like:
<% form_for #user do |f| %>
<%= #user.name %><br />
<% Car.all.each do |car| %>
<%= car.name %><br />
<%= f.fields_for :rates do |r| %>
<%= r.number_field :rate %>
<% end %>
<% end %>
<% end %>
The problem is that I would like the Rate model to store data as follows:
USER_ID CAR_ID RATE
1 1 10
1 2 20
1 3 30
2 1 40
3 2 50
I cannot figure out how to properly build the fields_for helper to build the proper params for both the user_id and the car_id.
Something like:
user[car=1][rate]
user[car=2][rate]
I've tried being more explicit with the fields_for like this:
<%= r.fields_for 'user[car][rate]' %>
But it still doesn't build out the nested parameters properly. The car parameter is not correctly identified.
Any help would be appreciated! Thanks.
EDIT:
The controller action has to be under user. The example above has been shortened for brevity but other user-related attributes are available through the form so it has to use the users controller.
ANSWER:
I figured out a way to do it. I've added my own answer that explains it.
<% form_for #user do |f| %>
<%= #user.name %><br />
<%= f.fields_for :rates do |r| %>
<% Car.all.each do |car| %>
<%= car.name %><br />
<%= r.number_field :rate %>
<% end %>
<% end %>
<% end %>
This may be solution of your problem. Just check it.
The form is going to create a new rate instead of a new user, so the method should be in RatesController instead of UsersController.
With this logic the problem seems solved. You can write field_for rate[user] and field_for rate[car]
I think I've got it figured out.
In my controller, I've modified the build method as follows:
Car.all.each { |c| #user.rates.build(car_id: c.id) } if #user.rates.count == 0
Then, in my model, I need the following:
attr_accessible :rates_attributes
Finally, the fields_for block should look like this (remember, this is in the #user form object f):
<%= f.fields_for :rates do |r| %>
<%= r.hidden_field :car_id %>
<%= r.object.car.name %><br />
<%= r.number_field :rate %>
<% end %>
This builds the params hash properly and create the rate model entries when the form is submitted.
The check on existing user rates in the controller will ensure that the existing values are used in the form and new ones are not built (which I thought build took into consideration... ?).

Rails updating multiple models on a single form

Im writing a form which uses formtastic to manage the BusinessUnit model, however when creating a new BusinessUnit it also has to create a number of other record types. The associations between the models are as below:
class BusinessUnit < ActiveRecord::Base
has_many :business_unit_sites
has_many :locations
class BusinessUnitSite < ActiveRecord::Base
belongs_to :site
belongs_to :business_unit
class Site < ActiveRecord::Base
has_many :locations
has_many :business_unit_sites
class Location < ActiveRecord::Base
belongs_to :business_unit
belongs_to :site
When a BusinessUnit is created, a Site must also be created using BusinessUnitSite as a join table. In addition a Location record should be created which must hold a foreign key to the new Site record and this is where Im having problems.
I can create a new Location using a nested form (below) but the Site will have to be created manually.
<%= semantic_form_for #business_unit do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<%= f.input :business_unit_id %>
<%= f.input :business_unit_group, :include_blank => false %>
<%= f.input :business_unit_type %>
<%= f.input :tax_region, :include_blank => false %>
<%= f.semantic_fields_for :locations do |l| %>
<%= l.input :name, :label => "Location Name" %>
<% end %>
<% end %>
<%= f.buttons %>
<% end %>
What is the best way to create the Location, Site records and ensure that Location holds the foreign key of the newly created Site?
You probably want to do something like using the "fields_for" approach for the sub-objects in your form.
See this related answer:
Multiple objects in a Rails form
More info about fields_for:
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for

Resources