I have a one to one relationship with Shares and Testimonials.
In Testimonial.rb:
belongs_to :share
In Share.rb:
has_one :testimonial
I want to add a form to the Shares page where I can create a testimonial which belongs to that specific share.
Inside the SharesController I set:
#testimonial = #share.build_testimonial
In the Shares view I have:
<%= form_for #testimonial do |f| %>
<%= f.text_area :message %>
<%= f.submit "Submit testimonial" %>
Is the above correct or do I have to add the share object to the view somehow?
What do I add to the create action in the Testimonials controller to create the testimonial and associate it with the #share object?
I have tried to send the share_id to the Testimonials controller from the view as an additional param and then used a "before" filter to find the share object but I don't think that's the right way to do it.
In the share.rb model, you'd want to add:
accepts_nested_attributes_for :testimonial
Since you are in the share view, I would think you'd have a construct in that view of:
<%= form_for #share do |f| %>
...
<% end %>
In that context, to use the testimonial fields:
<%= form_for #share do |f| %>
<%= fields_for #share.testimonial do |t| %>
<%= t.text_area :message %>
<% end %>
<% end %>
Related
I've been trying to create a form that would get parameters for multiple models. I have a photo model that belongs to a product model and I want to make it so that when you create a new product you can also upload images that are linked to that product by id.
<%= form_for #product, html:{multipart:true} do |f| %>
<div class="field">
<%= f.label :price %>
<%= f.text_field :price %>
</div>
<%=form_for #photo do |t| %>
<%t.productID = f.id%>
<div class="field">
<%= t.label (:image) %>
<%= t.file_field (:image) %>
</div>
<%end%>
<div class="actions">
<%= f.submit %>
</div>
<%end%>
right now I'm using paperclip for image attachments and the photo model accepts the images as parameters. I've used paperclip before but the product could only have one image connected to it. If I use the form above I get "First argument in form cannot contain nil or be empty" error and it points to where the form_for #photo starts.I have controllers for both with the usual methods of new, create, update, etc. I've routed resources to both product and photos but I'm still pretty new to rails and don't fully understand how this stuff works.
I think what you're trying to do is a good application for nested forms using the fields_for helper.
First, you'll need to ensure that your product model and photo model have the right associations (A product probably has_many photos, and a photo belongs to a product, right?). Then you'll make sure the product class 'accepts nested attributes for photo's which allows you to add attributes to the photos model from a products form.
in products.rb
class Product
has_many :photos
accepts_nested_attributes_for :photos
end
and in photo.rb
class Photo
belongs_to :product
end
Then you'll want to make sure any attributes you need for the photo are white-listed in your product params.
in products_controller.rb
private
def product_params
params.require(product).permit(:first_product_attribute, :second_produtc_attribute, photo_attributes: [:image])
end
Last, you'll create the form using the special helper fields_for
in your view
<%= form_for #product, html:{multipart:true} do |f| %>
<div class="field">
<%= f.label :price %>
<%= f.text_field :price %>
</div>
<%= f.fields_for :photo do |t| %>
<div>
<%= t.label :image %>
<%= t.file_field :image, :multiple => true %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<%end%>
You'll also need to make sure you're actually creating new photo objects in your product's create action:
in products_controller.rb
def create
#product = Product.new(product_params)
if #product.save!
params[:photo]['image'].each do |img|
#photo = #product.photos.create!(:image => img)
end
flash[:success] = 'product saved!'
redirect_to #product
end
end
Some of this is based on my experience doing the same thing but with Carrierwave instead of Paperclip so your specific implementation might be a little different.
I dont think this is a proper method <%t.productID = f.id%>. Maybe try <% t.text_field :productID, value = f.id, type = hidden %> or something along those lines?
heres some docs for the form helper so you know what to put after t.abcd
http://apidock.com/rails/v3.2.3/ActionView/Helpers/FormHelper/form_for
You're getting the
"First argument in form cannot contain nil or be empty"
..error because #photo is nil, you need to set it in your controller #photo = Photo.new.
Also, form tags inside form tags are invalid HTML.
https://www.w3.org/TR/html5/forms.html#the-form-element
Forms
Content model: Flow content, but with no form element
descendants.
You want to use f.fields_for instead. Learn how to use it here here
I have controllers for both with the usual methods of new, create,
update, etc.
You only ever hit one controller and action when you go to a path, say /photos will only hit the photos controller (as configured in your routes.rb). This I think is where you're messing up the #photo variable. Set both in the same controller in order for the view to be able to see both variables.
I have an application that deals with user rotas - and I'm currently adding the ability for admin approvals. If the user updates their own rota the params hash looks something like:
Parameters: {id:1, role_id: 1, team_id:1, rota: [startDate: 01/01/2014, endDate:02/02/2014]}
and these are submitted using a form with:
<%= form_for [#team,#role,#rota] do |f| %>
form code
<% end %>
We need to access the attributes outside the rota: object but currently can't find a way to as:
params.require requires you to pass an object in.
My team members have decided to add hidden fields to submit the attributes within the rota object but that seems redundant seeing as they are quite clearly there, we just can't find a way to access them, and ideas?
I was talking about something like
def user_rota_params
params.require(:user)
.permit(:role_id, :team_id, :rota => [:startDate, :endDate])
end
Then, for your nested attributes you could use fields_for.
Your form, thus, should look something this (omitting labels and keeping it basic):
<%= form_for #user do |f| %>
<%= f.text_field :role_id %>
<%= f.text_field :team_id %>
<%= f.fields_for :rota do |ff| %>
<%= ff.date_field :startDate %>
<%= ff.date_field :endDate %>
<% end %>
<% end %>
I broke out my form fields for a associated model into a partial. I want to reference the associated model in the partial but I'm not sure how to do that. #position and #position_fields are both nil. I see render ..., object: #something but I am not sure how to reference the current position model to pass it in.
apps/views/events/_form.html.erb
<%= f.fields_for :positions do |builder| %>
<%= render 'position_fields', f: builder %>
<% end %>
apps/views/events/_position_fields.html.erb
Fields for <%= #position.name %>
<%= f.text_field :name %>
How do I reference the associated model in the fields_for partial?
In your apps/views/events/_position_fields.html.erb, you can do the following:
Fields for <%= f.object.name %>
<%= f.text_field :name %>
This is because the builder has a reference to each of the position object and that you are passing builder as a local to the position_fields partial with the name f.
Reading through this doc will be useful: http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for.
I have what seems like a simple query. I need to create a view that will accept multiple records based on a single model. In my case the model is Project, which has 1 foreign key (person) and 2 fields time, role. I need to create a view (form) to insert 5 roles.
<%= form_for(#project) do |f| %>
<% 5.times do |index|%>
<div class="field">
<%= f.label :position %><br />
<%= f.text_field "fields[#{index}][stime]" %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I get an error message: undefined method `fields[0][stime]'
I do not think the railscasts for nested models is what I need.
How would I go about creating this?
EDIT: The Project model code is below:
class Project < ActiveRecord::Base
belongs_to :person
attr_accessible :role, :stime
end
The Projects_Controller code for the new method is below:
def new
#project = Project.new
end
I see you're planning to make some 1-to-many relationship (Product has_many :roles).
Here's some advices.
First, take a look at the accepts_nested_attributes_for method. You need to add it to your model to be able to perform mass-create.
Second, fields_for is what you need to design nested forms.
I'll give you some example of mass-creating for a simple Product has_many :line_items case:
<%= form_for #product do |f| %>
<%= f.fields_for :line_items, [LineItem.new]*5 do |li_fields| %>
<%= li_fields.text_field :quantity %>
<%= li_fields.text_field :price %>
<br>
<% end %>
<%= f.submit "Create line items" %>
<% end %>
All you need is to write in you controller something like:
#product.update_attributes params[:product]
and 5 line_items will be created at once.
Don't forget to white-list association_attributes (see params in your logs to see it). But I think if you get the mass-assignment error you'll do it anyway :)
I hope it helps.
On the front page of my rap lyrics explanation site, there's a place where users can try explaining a challenging line:
alt text http://dl.dropbox.com/u/2792776/screenshots/2010-02-06_1620.png
Here's the partial I use to generate this:
<div class="stand_alone annotation" data-id="<%= annotation.id %>">
<%= song_link(annotation.song, :class => :title) %>
<span class="needs_exegesis"><%= annotation.referent.strip.gsub(/\n/, "\n <br />") %></span>
<% form_for Feedback.new(:annotation_id => annotation.id, :created_by_id => current_user.try(:id), :email_address => current_user.try(:email)), :url => feedback_index_path, :live_validations => true do |f| %>
<%= f.hidden_field :annotation_id %>
<%= f.hidden_field :created_by_id %>
<p style="margin-top: 1em">
<%= f.text_area :body, :rows => 4, :style => 'width:96%', :example_text => "Enter your explanation" %>
</p>
<p>
<% if current_user %>
<%= f.hidden_field :email_address %>
<% else %>
<%= f.text_field :email_address, :example_text => "Your email address" %>
<% end %>
<%= f.submit "Submit", :class => :button, :style => 'margin-left: .1em;' %>
</p>
<% end %>
</div>
However, putting more than one of these on a single page is problematic because Rails automatically gives each form an ID of new_feedback, and each field an ID like feedback_body (leading to name collisions)
Obviously I could add something like :id => '' to the form and all its fields, but this seems a tad repetitive. What's the best way to do this?
If you don't want to change your input names or your model structure, you can use the id option to make your form ID unique and the namespace option to make your input IDs unique:
<%= form_for Feedback.new(...),
id: "annotation_#{annotation.id}_feedback"
namespace: "annotation_#{annotation.id}" do |f| %>
That way our form ID is unique, i.e. annotation_2_feedback and this will also add a prefix, e.g. annotation_2_, to every input created through f.
Did you consider nested_attributes for rails models? Instead of having multiple new feedback forms where each is tied to an annotation, you could have multiple edit annotation forms where each annotation includes fields for a new feedback. The id's of the generated forms would include the annotations id such as edit_annotation_16.
The annotation model would have a relationship to its feedbacks and will also accept nested attributes for them.
class Annotation < ActiveRecord::Base
has_many :feedbacks
accepts_nested_attributes_for :feedbacks
end
class Feedback < ActiveRecord::Base
belongs_to :annotation
end
You could then add as many forms as you want, one for each annotation. For example, this is what I tried:
<% form_for #a do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
<% form_for #b do |form| %>
Lyrics: <br />
<%= form.text_field :lyrics %><br />
<% form.fields_for :feedbacks do |feedback| %>
Feedback: <br/>
<%= feedback.text_field :response %><br />
<% end %>
<%= form.submit "Submit" %>
<% end %>
And the quick and dirty controller for the above edit view:
class AnnotationsController < ApplicationController
def edit
#a = Annotation.find(1)
#a.feedbacks.build
#b = Annotation.find(2)
#b.feedbacks.build
end
def update
#annotation = Annotation.find(params[:id])
#annotation.update_attributes(params[:annotation])
#annotation.save!
render :index
end
end
I had this same issue on a site I'm currently working on and went with the solution you mention at the bottom. It's not repetitive if you generate the ID programmatically and put the whole form in a partial. For example, on my site, I have multiple "entries" per page, each of which has two voting forms, one to vote up and one to vote down. The record ID for each entry is appended to the DOM ID of its vote forms to make it unique, like so (just shows the vote up button, the vote down button is similar):
<% form_for [entry, Vote.new], :html => { :id => 'new_up_vote_' + entry.id.to_s } do |f| -%>
<%= f.hidden_field :up_vote, :value => 1, :id => 'vote_up_vote_' + entry.id.to_s %>
<%= image_submit_tag('/images/icon_vote_up.png', :id => 'vote_up_vote_submit' + entry.id.to_s, :class => 'vote-button vote-up-button') %>
<% end -%>
I also had the same issue but wanted a more extensible solution than adding the ID to each field. Since we're already using the form_for ... |f| notation the trick is to change the name of the model and you get a new HTML ID prefix.
Using a variant of this method: http://www.dzone.com/snippets/create-classes-runtime (I removed the &block stuff)
I create a new model that is an exact copy of the model I want a second form for on the same page. Then use that new model to render the new form.
If the first form is using
#address = Address.new
then
create_class('AddressNew', Address)
#address_new = AddressNew.new
Your ID prefix will be 'address_new_' instead of 'address_' for the second form of the same model. When you read the form params you can create an Address model to put the values into.
For those stumbling here, looking for the solution for Rails 3.2 app, look at this question instead:
Rails: Using form_for multiple times (DOM ids)