How to pass foreign key attributes down through a nested form in Rails 3 - ruby-on-rails

I have three models:
class Client < ActiveRecord::Base
has_many :balancesheets
has_many :investment_assets, :through => :balancesheets
class Balancesheet < ActiveRecord::Base
belongs_to :client
has_many :investment_assets, :dependent => :destroy
accepts_nested_attributes_for :investment_assets, :allow_destroy => true
class InvestmentAsset < ActiveRecord::Base
belongs_to :balancesheet
belongs_to :client
I have two questions that have to do with the foreign key client_id. First, when I create a new balancesheet, I use collection_select to select the client from the list. I would rather put the new balancesheet link on the client show page and just pass the client id through to the form so I don't have to choose a client from a list or type it in a field. So first, how do I do that?
Second, the investment_asset model is nested in the balancesheet form. Everything works just fine, except the client_id attribute for the investment_asset is blank. Im not sure why because my associations seem to be okay. So the question is, how do I pass this client_id attribute down through the nested form? I can post models or controllers, just let me know.
UPDATE I have since found the answer to my first question here. However, I am still trying to figure out the second part based on that answer, which is, how do I pass this client_id down through a nested form? To show how it worked passing user id when creating a balancesheet, here is the client show page link:
<%= link_to 'New BalanceSheet',new_balancesheet_path(:id => #client.id) %>
In the balancesheet form I have a hidden field:
<%= f.hidden_field :client_id %>
And in the Balancesheet Controller this is the new action:
#balancesheet = Balancesheet.new(:client_id => params[:id])
This works just fine. So for an investment_asset I am using Ryan Bates nested_form gem which has a little bit different syntax for a link. So inside the balancesheet form the link to add a new investment_asset is:
<%= f.link_to_add "Add asset", :investment_assets %>
I can't figure out how to pass the user id like I did on the balancesheet with:
(:id => #client.id)
I can put the same thing in a new action in the investment_asset controller and add a hidden field, but I'm not sure how to pass it in on the link. Thoughts?

This might be a total hack for all I know, but all I know is very limited. What I ended up doing here was user jQuery to take the id from the balancesheet and force it into a hidden field for investment_asset. jQuery:
// get the value from the balancesheet hidden field that contains the user_id value
var balsheetclientid = jQuery('#hiddenid').attr('value');
// insert it into the value attribute in the hidden field with class name .iaclientid
jQuery('.iaclientid').val(balsheetclientid)
Then my hidden field looks like this:
<%= f.hidden_field :client_id, :class => 'iaclientid', :value => '' %>
It works just fine. Hopefully that's a valid way of doing it.

Related

Rendering nested form fields in Rails

In our app, we have, among others, these three models:
class PackContent < ActiveRecord::Base
belongs_to :product
belongs_to :pack
class Product < ActiveRecord::Base
has_many :pack_contents
has_many :packs, through: :pack_contents
class Pack < ActiveRecord::Base
has_many :pack_contents
has_many :products, through: :pack_contents
accepts_nested_attributes_for :pack_contents
A pack has multiple pack_contents, which are basically a product and some specific productvariants. The join table (pack_contents) looks like this:
I am now trying to display these pack_contents in a usable manner, but keep running into a wall.
I have tried:
.pack-product-rows
- #pack.pack_contents.each do |pc|
= f.simple_fields_for pc do |p|
= render :partial => 'pack_product_row', :locals => { :p => p, :pc => pc }
But then, upon saving, I get an error saying:
ActiveRecord::UnknownAttributeError in Admin::PacksController#update
unknown attribute: pack_content
And the form field name attributes are wrong, they don't account for multiple contents as they don't include an index.
When I try this:
.pack-product-rows
- #pack.pack_contents.each do |pc|
= f.simple_fields_for :pack_contents do |p|
= render :partial => 'pack_product_row', :locals => { :p => p, :pc => pc }
It renders every partial twice, which, I suppose, is to be expected.
If i don't loop over the contents and just use simple_fields_for, I get the output I want, but not how I want it. I can render the fields with p.input_field :id and the likes, but I need access to the pack_contents in order to display my data like this: (input fields meant to be hidden)
Not sure how to proceed.
I'm not sure if I've understood what you're asking but if you just want an access to the current item being automatically iterated via simple_fields_for, then you can access it through the form builder as p.object.
I am assuming that you are trying to build the nested form on the views/packs/show.html.erb where in you have the #pack instance variable initialized to a pack from the database. So on this page you will have a - simple_form_for #pack form inside which you want to nest over the pack_contents.
Now the simple_form_fields method of the simple_form gem works just like the fields_for method in rails. Based on that I am giving you the code below. It should work properly but you may have make a few syntactical changes to make it work.
- simple_form_for #pack do |pack_form|
.pack-product-rows
- #pack.pack_contents.each do |pack_content|
= f.simple_fields_for :pack_contents, pack_content do |pack_content_fields|
= render :partial => 'pack_product_row', :locals => { :pack => #pack, :pack_content => pack_content, :pack_content_fields => pack_content_fields}
Now in the partial pack_product_row you will need to use the three locals passed. You must be already using pack and pack_content but whatever fields for pack_contents you want to display should be written as pack_content_fields.field_name. If your naming convention is correct for all the fields then these fields will be either empty if the pack_content is empty or having the value stored in the database.

Pass a parameter to the new action in Active Admin

I have two related models, Bunny has_many BunnyData (which belongs_to Bunny). From the show page of a particular Bunny (in Active Admin), I want to create a link to make a related BunnyData. I've tried a few different ways, with no success, and am currently trying this:
sidebar :data, :only => :show do
link_to 'New Data', new_admin_bunny_datum(:bunny_id => bunny.id)
end
The link being generated ends up as something like:
.../admin/bunny_data/new?bunny_id=5
But when you go to that page, the dropdown for Bunny is set to the blank default as opposed to showing the name of Bunny with ID 5.
Thanks in advance.
Rails namespaces form fields to the data model, in this case BunnyData. For the form to be pre-filled, any fields provided must also include the namespace. As an example:
ActiveAdmin.register Post do
form do |f|
f.inputs "Post Details" do
f.input :user
f.input :title
f.input :content
end
f.actions
end
end
The fields can be pre-filled by passing a hash to the path helper.
link_to 'New Post', new_admin_post_path(:post => { :user_id => user.id })
Which would generate the following path and set the form field.
/admin/posts/new?post[user_id]=5
In the case of BunnyData, it might be slightly different due to the singular and plural forms of datum. But that can be verified by inspecting the generated HTML to find the name attribute of the inputs.

Rails: Drop down with 'Add new' option which creates more form fields

I want to have a drop down on a form. So for example a user is picking an equipment type. But if it is not in the dropdown I want there to be an option for 'Add New' and when this is clicked some more fields pop up that the user must fill in.
So essentially when the form is submitted it creates two new model objects: Equipment and EquipmentType.
Is this possible?
I'm not sure what you exactly need, so I point you to two solutions:
When you clicks "Add new" and you need to fill more then one field.
This is the standart situation for this gems: nested_form and cocoon.
Example of how it looks: http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast
When you clicks "Add new" and just want to add another option to
has_many association. In this case you can transform the standart
multiple select into that kind of behavior. Take a look at that
question: https://stackoverflow.com/questions/2867795/best-jquery-multiselect-plugin, there are a couple plugins for this.
Example: http://odyniec.net/projects/selectlist/examples.html
As far as I know, there is still no gem for it. I assume you have two models, Equipment, and EqipmentType, each of them has :name and they are connected via belongs_to/has_many association.
My solution will add an attr_accessor :new_equipment_type to Equipment, which is populated after the form is submitted.
Then, it creates a new equipment type if needed and connects it to the equipment:
# app/models/equipment.rb
class Equipment < ActiveRecord::Base
belongs_to :equipment_type
attr_accessible :name, :equipment_type_id, :new_equipment_type
attr_accessor :new_equipment_type
def new_equipment_type=(val)
if equipment_type_id.blank?
write_attribute(:equipment_type_id, EquipmentType.create(name: val).id)
end
end
end
# app/models/equipment_type.rb
class EquipmentType < ActiveRecord::Base
has_many :equipments
attr_accessible :name
end
The form has a dropdown with existing equipment types and when a blank option is selected, new input is shown for a new equipment type name:
#app/views/equipments/_form.html.haml
= simple_form_for [#equipment] do |f|
= f.input :name
= f.association :equipment_type, include_blank: "Add new"
= f.input :new_equipment_type, input_html: {value: "New equipment type name"}, wrapper_html: {style: "#{if #equipment.equipment_type_id.present? then 'display:none;' else '' end}"}
And finally javascript to show/hide the new equipment type field:
#app/assets/javascripts/equipments.js.coffee:
#showOrHideNewEquipmentTypeField = (drop_down) ->
new_equipment_type = $(drop_down).closest('form').find('div.input[class*=new_equipment_type]')
if $(drop_down).val()
$(new_equipment_type).slideUp()
else
$(new_equipment_type).slideDown()
$ ->
$('[name*=equipment_type_id]').change ->
showOrHideNewCategoryField(#)

an example of a nested form in simple form?

I am still struggling both writing the controller and the actual form to be able to nest one form in another with an optional model?
I have Message which has many contacts
When submitting a message, I want to add a contact optionally.
I have this as an example:
= simple_form_for Message.new, :remote => true do |f|
#message_form
= f.error_messages
%p
= f.input :account_name, :url => autocomplete_account_name_messages_path, :size => 40, :as => :autocomplete
%p
= f.input :topic, :required => true,
:input_html => {:size => 30}
#add_contact_btn
= link_to "Add Contact"
#contact_form
= f.simple_fields_for :contactd do |fc|
= fc.input :email
= fc.input :first_name
= fc.input :last_name
= f.submit 'Give'
= f.submit 'Request'
For Message.rb model, I have the following:
has_many :contacts
accepts_nested_attributes_for :contacts, :reject_if =>:all_blank
Note: When I used :contacts in the simple_fields_for it didn't work, so it is singular. But the reverse for accepts_nested_attributess_for.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Here is what I see passed as form data from google chrome:
message%5Baccount_name%5D:McKesson
message%5Btopic%5D:testing a contact
message%5Bbody%5D:testing this
sender_id:
receiver_id:23
message%5Bcontacts%5D%5Bemail%5D:888#gmail.com
message%5Bcontacts%5D%5Bfirst_name%5D:Ang
message%5Bcontacts%5D%5Blast_name%5D:Name
The correct method name is simple_fields_for (notice the plural)
Also, you need to keep the f. to call it on the simple_form object
I have a small project where I demonstrate how to use nested forms in simple-form, combined with cocoon (a gem I created to add/remove nested elements dynamically).
The project is on github.
Hope this helps.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Make sure you put in your Message.rb model the ability for it to accept the attributes too.
class Message < ActiveRecord::Base
attr_accessible :contacts_attributes
has_many :contacts
accepts_nested_attributes_for :contacts
I know it doesn't answer your question fully but it may have just been this. When it comes to my project, it would return nil if i didn't include the :contacts_attributes, in my case it deals with products. Hope this helps even if I'm not using simple form as of now!
I faced similar issues working with nested forms. As suggested by JustinRoR you need to define
attr_accessible: contacts_attributes.
You should be able to test the hash in the ruby console ( I am not sure if you have tried this). I suggest you to print the params[:message] and use this to create message from console like Message.new(params[:message]). (Note params[:message] is what you get by printing the params[:message] hash).
Once it works in console it should work like charm

Validating auto complete input for associations

Ok, so I'm probably missing something obvious here. I'm using rails3-jquery-autocomplete and formtastic to allow the user to select a Category when creating an Item:
class Category < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :category
validates :category_id, :presence => true
end
I got this working via:
semantic_form_for #item do |form|
form.inputs do
form.input :category_id, :as => :hidden
form.autocompleted_input :category,
:url => autocomplete_category_path,
:id_element => "#item_category_id"
end
end
That all works fine and dandy; when the user selects the category name, the category_id goes into the hidden field, and the item saves appropriately.
However i run into problems validating when the user doesn't input/select a valid category. What is the appropriate way to validate the following cases:
User types something in to the box that is not valid category. (this needs to fail. It fails now, but the inline errors are actually on the hidden field)
User makes a selection, then changes the value of the text field to an invalid category (resulting in the id in the hidden field becoming stale. This should also fail, but instead it uses the stale selected Id)
Thanks!
It seems like you are looking for jQuery Tokeninput.
You just serve your categories via REST or include the JSON into the initialization
$("#mytokeninputfield").tokenInput("/categories.json",
{
tokenLimit: 1 // allow at most 1 item
});
<input type="text" id="mytokeninputfield" name="item[category_tokens]" data-pre="<%= #user.category_id %>"/>
See railscast #258 on tokenfields and my gem tokenizes as well (although I currently don't use it feel free to report bugs and I'll take care of it. Or just get inspired and do it on your own :) )

Resources