I am using the cocoon gem to try and achieve adding an object which belongs to another with nested fields. I have a 'user_resolution' which has many 'milestones'. I have set up the associations accordingly in both of these models. For some reason, milestones are failing to be created, however if I add one manually in the database I can successfully update it. I am able to dynamically add the fields and remove them using the cocoon gem but that is all. When I click 'add milestone' it redirects me to the show view of the user resolution and throws the success message saying user resolution has been updated, no errors are thrown but the milestone(s) is/are not created.
user_resolution.rb
has_many :milestones
accepts_nested_attributes_for :milestones, reject_if: :all_blank, allow_destroy: true
milestone.rb
belongs_to :user_resolution
I have set up the nested form within the edit view as for now I only want users to add a milestone to a resolution in the edit view.
user_resolutions/edit.html.erb
<%= form_for(#user_resolution) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%= f.fields_for :milestones do |milestone| %>
<%= render 'milestone_fields', f: milestone %>
<% end %>
<%= link_to_add_association 'Add Milestone', f, :milestones %>
<%= f.submit "Add Milestone" %>
<% end %>
_milestone_fields.html.erb
<div class="nested-fields">
<div class="field-row">
<%= f.label :name, 'Name' %>
<%= f.text_field :name %>
</div>
<div class="field-row">
<%= f.label :description, 'Name' %>
<%= f.text_area :description %>
</div>
<div class="field-row">
<%= f.label :severity, 'severity' %>
<%= f.check_box :severity %>
</div>
<div class="field-row">
<%= f.label :target_date, 'target_date' %>
<%= f.date_select :target_date %>
</div>
<%= link_to_remove_association 'Remove', f %>
</div>
The permitted parameters within the user resolutions controller also contain the following
milestones_attributes: [:id, :user_resolution_id, :name, :description, :target_date, :severity, :complete, :_destroy]
The milestones themselves have no views, they only have a model and a controller. The controller create action (which i'm unsure is required for nested forms) contains the standard following code
def create
#milestone = Milestone.new(milestone_params)
if #milestone.save
redirect_to user_resolutions_path,
:flash => { :success => "You successfully created a milestone" }
else
redirect_to new_milestone_path,
:flash => { :error => "Oops something went wrong. Try again." }
end
end
I've been as informative as I can but if you need anything else let me know. Thanks guys.
which i'm unsure is required for nested forms
You don't need a create action for milestones - they'll be populated from the user_resolutions#create controller action.
There are several things to look at with this. I'll detail some here. This won't be a specific answer, but may help point you in the right direction.
Firstly, you need to make sure you're receiving the correct params.
Cocoon does a great job building the nested form - you need to make sure it's obliging Rails' nested attribute structure.
To do this, you should right-click > view source.
In the f.fields_for section (it won't be called that in the HTML), you'll be looking for the equivalent to the following:
<input type="text" name="milestones_attributes[0][name]" value="">
The important thing to note is the name...
Each time you use a form, or any Rails view helper for that matter, you're really just building standard HTML. form_for just creates an HTML form, and thus any params contained within it need to adhere to a certain structure for Rails to recognize the params.
The f.fields_for elements will typically be called x_attributes[:id][:param] - this is passed to Rails, which cycles through each [:id] to determine the number of nested params to add.
You need to check the source for the above naming structure. If you see it, that's good. If not, it means you haven't built your form properly.
Secondly, you need to make sure your objects are being built in the controller.
I'm not sure how Cocoon does this, but essentially, each time you use f.fields_for, you have to build the associated object before:
def new
#user_reservation = UserReservation.new
#user_reservation.milestones.build #-> this is what makes f.fields_for work
end
If the first step shows incorrect element naming, it means your associative objects are not being built (which is why they're not being recognized).
To test it, you should build the associative objects in the new method, before sending.
Finally, you'll want to post your params.
These tell you in explicit detail what Rails is doing with the nested attributes, allowing you to determine what's happening with them.
Sorry for the long-winded answer. You'll not have received any answers anyway, so I felt it prudent to give you something.
Related
I'll try to ask this as clearly as I can. I'm doing a Udemy course and we are using nested fields to take in data. The form renders a single nested field initially and then there is a link to add additional fields. This all works and there are no errors in the console. If i put something in the first nested field, and add two more nested fields, only what was entered in the last two nested fields gets saved. When I look at the final result after saving, the first item is not there. I'm sure this has to do with how I'm rendering the initial field, but I can't figure out what it is. Both the initial render and the link_to_add_nested are using the same partial file.
Screenshot of the form
Code from _form.html.erb:
<div>
<%= form_with model: #technologies do |form| %>
<%= render partial: 'technology_fields', :locals => { :form => form } %>
<% end %>
</div>
<div>
<%= link_to_add_nested(form, :technologies, '#Add-Technology', partial: 'technology_fields') %>
</div>
<div class="form-group">
<%= form.submit "Save Portfolio Item", class: 'btn btn-primary btn-block'%>
</div>
<% end %>
</div>
Code from the partial _technology_fields.html.erb:
<div class="form-group nested-fields" id=Add-Technology>
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
<%= link_to_remove_nested(form, link_text: 'Remove Item') %>
</div>
I can add code from the models and controller if needed, but as the form is working with the dynamically added nested fields, I don't think that's where the problem lies.
Bonus question: The same form is used for new and edit actions. Ideally when the form is used to edit an existing item, I would like the page to render with fields populated with the existing data. As it currently stands I have no way to delete or modify once it has been saved. I can only add new entries.
Thank you very much for any help you can give. I've done a lot of searching but haven't been able to locate the right information.
I'm making an app where some activities are listed in a table called Fakultety (polish language, sorry), and participants on in another table called Uczestnicy.
I have a submit form where you can submit yourself to an activity, but I'm stuck on passing values to a DB. Firstly, I don't know how to tell to the database on which activity you want to be assigned to (I tried to change the submit button id to an activity id and then passing it into a database but don't know how to do this id: "#{#fakultet.id}" not working) and later I want to count how many people are assigned to field participants in a database Fakultety, but I don't want to pass all the data, just ID of the users from table called Uczestnicy. How to do it? I mean just to pass the ids to another table, and how to tell the database on which activity I want to be assigned to?
This is my form view:
<h1>Zapisujesz sie na fakultet</h1>
<div class="form-group">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#participant, url: zapisy_path) do |f| %>
<p>Imię:</p>
<%= f.text_field :firstName, class: 'form-control' %>
<p>Nazwisko:</p>
<%= f.text_field :lastName, class: 'form-control' %>
<p>Grupa:</p>
<%= f.text_field :group, class: 'form-control' %>
<%= f.submit "Zapisz się", class: "btn btn-primary" id: "#{#fakultet.id}"%>
<% end %>
</div>
</div>
</div>
Does anybody understand me and can help me?
Rails provides form collection helpers that make it really easy to assign associations:
# I'm going to just do this in english
<%= form_for(#activity) do |f| %>
<%= f.collection_select(:participant_ids, Partipicant.all, :id, :name, prompt: true, multiple: :new) %>
# ...
<% end %>
Then whitelist the attribute as an array:
params.require(:activity).permit(:foo, :bar, participants_ids: [])
Thats all you actually need to assign childen to to a parent resource. This is done as a part of the normal create/update actions of the parent resource.
You don't actually need the form for the child records unless you actually want to be creating the record. In that case you can setup a nested resource or if you want to create/edit multiple nested records at the same time as the parent record you can use nested attributes.
First you should rename your models and tables, to English, it's a really bad pattern to use your language, in English it is easier to understand by other devs.
According to the problem, probably what you are looking for is hidden_field
<%= f.hidden_field :fakultet_id, :value => #fakultet.id %>
and if you want to query Fakultety that have user assigned, you can select Fakultety where participant_id is not nil
Fakultety.where.not(participant_id: nil)
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 a relatively simple address partial that I use for 'new' and 'edit' forms for when an address is needed on an object. It works great. The problem is now that I want the same fields to show up on my 'show' views. The only problem is that my address partial requires a form which isn't present in a 'show' view. I would duplicate it, but that doesn't seem very DRY to have one partial for new/edit and another for show.
I was wondering if anyone has solved this problem. I have thought about using .new? and .persisted? but that doesn't really help because an object for editing will pass both of those in the same way a show object would.
Any help is greatly appreciated.
EDIT: Here is my address partial for new and edit. I would like to use the same partial as in the new,edit and show view, as recreating an entirely new partial doesn't seem very DRY. The issue I see is that this partial requires a form :f, which of course isn't available in a 'show' view.
<p>
<%= f.label :address_line_1, 'Address 1' %><br />
<%= f.text_field :address_line_1 %>
</p>
<p>
<%= f.label :address_line_2, 'Address 2' %><br />
<%= f.text_field :address_line_2 %>
</p>
<p>
<%= f.label :city %><br />
<%= f.text_field :city %>
</p>
<p class="address_state">
<%= f.label :state , "State" %><br />
<%= f.select :state, us_states, :include_blank => true %>
</p>
<p>
<%= f.label :zip_code, 'Zip Code' %><br />
<%= f.text_field :zip_code %>
</p>
I have the same concern, wanting to DRY out not just new/edit but show as well. With a lot of attributes on a model, it becomes a pain to maintain them in parallel across multiple views.
Poked around, could not find anything, except this (unresolved) post. And #Valery, isn't fields_for really meant for editing other models inside your form_for?
#FlexFiend, I ended up solving your problem this way (and admittedly, it's clunky, but it gets the job done): DO use a form also for the show view, and create a partial (shared by all actions) that calls helpers that test the controller action, and if "editable", then invoke the right control type. Otherwise else just render the attribute if it's a show action.
For example, the starting template (show, new, edit) simply invokes a form partial:
<%= render :partial => 'form' %>
...where _form is generic:
<%= form_for(#myObject) do |f| %>
<div>
<%= render :partial => "fields_for_all_actions", :locals => { :f => f } %>
</div
<% end %>
...and inside the partial, to either show or edit a 'name' attribute use a helper 'render_field':
<div><%= f.label :name %><span><%= render_field("#facility",f,"name", 30)%></span></div>
Inside the helper, render_field looks like this:
def render_field(object_name, f, attribute, txt_size=20)
if %w[new edit create update].include? controller.action_name
f.text_field(attribute.to_sym, :size => txt_size)
else
eval(object_name + "." + attribute)
end
end
I have created other helpers for other controls (text areas, radios, checkboxes, etc). These helpers are not model-specific, they live in application_helper.rb and can be used by multiple models.
It works, but I would be curious to know of other solutions to this problem, as the maintenance challenge of larger models is not insignificant. Granted, that argues perhaps for refactoring the model into component models, but that's another post.
Include a readonly attribute in your fields, something like:
<%= f.text_field :name, readonly: #readonly %>
then place #readonly = true in your show method.
<%= fields_for #object do |f| %>
<%= render 'your/fields_parial', :f => f %>
<% end %>
I have a Client model that can have many Projects.
These are my view files:
edit.html.erb
<%= form_for(#project) do |f| %>
<%= render 'fields', :f => f %>
<%= f.submit Create %>
<% end %>
_fields.html.erb
<div>
<%= f.label :name %><br/>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :client_id %><br/>
<%= f.select(:client_id, current_user.client_names) %>
</div>
This is easy and works great.
But is there a way to create a Project from within a Client view as well?
For example on the client edit page it would be nice to have a link New Project for this Client that leads to the above New Project form, but with the respective client preselected in the select box.
Can this be done somehow?
I can't seem to find a way to pass the Client ID to the New Project form.
Thanks for any help!
This is certainly possible, but requires passing the client id to the new_projects_path. This can be done by nesting the routes to projects inside of clients, or just appending the client id. The difference would be URLs that look like the following:
/clients/1/projects/new
- or -
/projects/new?client_id=1
In your projects controller, you should be able to instantiate the #project variable with a preselected client:
#project = Project.new :client_id => params[:client_id]
Yes, you can do this. You're going to use accepts_nested_attributes_for :project in your Client model. Nesting this will allow you to do something like
<%= f.fields_for :project do |p| %>
<fieldset>
<%= p.text_area :content %>
</fieldset>
<% end %>
Check out the railscasts episode #196 on Nested Model Form.