I have 2 models:
class Store < ActiveRecord::Base
attr_accessible :prices_attributes, :business_name
has_many :prices
accepts_nested_attributes_for :prices
end
class Price < ActiveRecord::Base
attr_accessible :price, :product_name, :purchase_date
belongs_to :store
end
I'm creating a Store and Prices together making it a nested form:
class StoresController < ApplicationController
def new
#store = Store.new
3.times {#store.prices.build }
end
end
Nested form:
<%= form_for #store do |f| %>
<%= f.text_field :business_name %>
<%= date_select("price", "purchase_date") %>
<%= f.fields_for :prices do |up| %>
<%= up.text_field :product_name %>
<%= up.text_field :price %>
<% end %>
<% end %>
I want to place the purchase_date outside of my f.fields_for so users only have to choose one date_select for all prices made. This doesn't work though. The purchase_date does not show up on the form. How can I get it do this?
If the child forms display the purchased_date and purchased inputs - and you want to set the values of those inputs based on the input values in the parent form - you're going to have to script that on the client side (javascript/jquery) so the children's values get set in response to the parent input's change event (or similar).
If the child forms do not show these inputs, you will have to set the children's attributes on the server side (model or controller) based on the parent's attributes (if you choose to add them to the parent) or by using the params sent on the request.
Related
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 %>
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
I have a form where a student can enroll in a class. When the user submits the form, he is enrolled in the class and his payment information is saved. In other words, an Enrollment object is created and the Student object is updated... except I cannot get the Student to update. Is this possible? If so, how?
My models...
class Student < ActiveRecord::Base
has_many :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :student
accepts_nested_attributes_for :student
end
My (abbreviated) form...
<%= form_for :enrollment, html: { id: "enrollment_form" } do |f| %>
<%= f.fields_for :student_attributes do |student_builder| %>
<%= student_builder.hidden_field :payment_name %>
<% end %>
<%= f.hidden_field :payment_token %>
<div class="field terms">
<%= f.check_box :agreed_to_terms %>
<%= f.label :agreed_to_terms, "I agree to the terms and conditions." %>
</div>
<% end %>
My controller...
class EnrollmentsController < ApplicationController
def create
#enrollment = Enrollment.new(enrollment_params)
#enrollment.clazz_id = #clazz.id
#enrollment.student_id = #student.id
#enrollment.save
end
private
def enrollment_params
params.require(:enrollment).permit(:payment_token, :agreed_to_terms, student_attributes: [:payment_name])
end
end
The POST parameters...
{
"enrollment"=> {
"student_attributes"=> {
"payment_name"=> "MasterCard ending in 9840"
},
"payment_token"=> "CC11ho86XxVqsUW7Cn9YjCHg?1376007969212",
"agreed_to_terms"=> "1"
},
"clazz_id"=> "7"
}
I've tried every permutation of student, students, _attributes in the form builder but none of them seem to work.
Ok, there are a few things I see:
Nested attributes, as described in the API, "allow you to save attributes on associated records through the parent." This means that
class Enrollment < ActiveRecord::Base
belongs_to :student
accepts_nested_attributes_for :student
end
inherently won't work because you're trying to accept nested atributes from the parent. So you need to start by rethinking your Active Record assications.
If we pretend that's all squared away, then more syntactical errors are:
Replace
<%= f.fields_for :student_attributes do |student_builder| %>
with
<%= f.fields_for :students do |student_builder| %>
This can be confusing, but passing :students to the fields_for helper calls the nested student object, whereas :student_attributes is the hash key from the POST parameters that fields_for produces.
In your strong parameters, you also need to permit the student :id so that your updating action has a reference. Otherwise, it will just create a new student. So change it to
private
def enrollment_params
params.require(:enrollment).permit(:payment_token, :agreed_to_terms, student_attributes: [:payment_name, :id])
end
I'm not sure if that's everything, but hopefully it's a start. Good luck.
I'm trying to build a rather complex nested form in rails and am stuck.
Basically, I have three models - Applicant, DataPoint, ApplicantDataPointValue .
The user can create a new DataPoint, give it a name ("gender" etc.) select it's type ("string","integer" etc.). The type determines what column the data will eventually be saved in in the ApplicantDataPointValue table.
I then want the user, when they're creating a new Applicant, to be able to add a value for each DataPoint into the ApplicantDataPointValue table
My models look like the following:
Applicant:
class Applicant < ActiveRecord::Base
has_many :applicant_data_point_values, dependent: :destroy
has_many :data_points, :through => :applicant_data_point_values
accepts_nested_attributes_for :data_points
accepts_nested_attributes_for :applicant_data_point_values
attr_accessible :data_points_attributes, :applicant_data_point_values_attributes
end
DataPoint:
class DataPoint < ActiveRecord::Base
has_many :applicant_data_point_values
has_many :applicants, :through => :applicant_data_point_values
accepts_nested_attributes_for :applicant_data_point_values
end
ApplicantDataPointValue:
class ApplicantDataPointValue < ActiveRecord::Base
belongs_to :data_point
belongs_to :applicant
end
But I'm at a loss to what to do in the 'new' and 'create' sections of my controller or how to construct the form.
Any insight would be greatly appreciated.
From what I understand, the form for the User will also have multiple ApplicantDataPointValue fields. (but that form won't allow creating of new DataPoint fields, right?)
In the controller new action, you'll want to set up your model with associated data point values:
def new
#user = User.new
DataPoint.all.each do |data_point|
applicant_data_point_value = #user.applicant_data_point_values.build
applicant_data_point_value.data_point = data_point
end
end
And then, display a text box for each data point value.
<%= form_for #user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<% #user.applicant_data_point_values.each do |data_point_value| %>
<%= f.fields_for :applicant_data_point_values, data_point_value do |fields| %>
<%= fields.label :value, data_point_value.data_point.type %>
<%= fields.text_field :value %>
<% end %>
<% end %>
Reference: http://railscasts.com/episodes/196-nested-model-form-part-1
If I have the models with the following associations:
class Business
has_many :products
class Product
belongs_to :business
And I generate 3 products in the controller:
def new
#business = Business.new
3.times do
#business.products.build
end
end
Making my form look like this:
<%= form_for #business do |f| %>
<% f.text_field :business_name %>
<%= f.fields_for :products do |pf| %> # x2 more products generated
<% pf.text_field :name %>
<% pf.text_field :price %>
<% pf.text_field :date %>
<% end %>
If I want one of the fields to act as a global field for the rest of the products how could I take a field like the :price and put it outside of the f.fields_for :products to have it be the :price for all of the products?
Thank you.
If you need to initialize the price, do it in the controller. But if you need a field that doesn't map to a model directly, use the regular form helpers:
<%= text_field_tag 'global_price' %>
and then in the controller on the create action, it is available as
params[:global_price]
Alternately, you could define a method in your Business model:
def global_price=
#do something with the global price, such as updating child object...
# I'm not sure if the child form objects have been instantiated yet though
end
and then you can use it in your business form:
<%= f.text_field :global_price %>
If you need to update the child objects, you might have to do that at a later time; instead of that method, make it
attr_accessor :global_price
Which makes it an instance variable. Then you can use a before_save filter to update the child objects.
before_save :update_global_price
def update_global_price
#do something with #global_price
end