Reputation: 1501
I have a model Product, which has a properties attribute. It stores it in a single database column as a hash (following http://api.rubyonrails.org/classes/ActiveRecord/Store.html)
class Product < ActiveRecord::Base
store :properties
end
How can I create dynamic form fields for this property attribute (which is a hash)? I'm interested in ideologically correct way of doing this ("rails way"). I guess that there is need to use fields_for helper. But I do not fully understand how to do it. Tell me the correct way of solving this problem, please.
In result, I'd like to get a working form like shown on image.
Where a user can add unlimited number of fields and give any property names and its values.
Upvotes: 12
Views: 10517
Reputation: 5802
To be brief, you want to have a form that contains this within it somewhere:
<% @product.properties_hash.each do |k,v| %>
<%= f.field_for k %>
<%= f.field_for v %>
<# link to action that will remove this key/value pair from the serialized hash saved in the database %>
<% end -%>
You're going to have to create a blank key and value pair somehow (either here in the form just using the form helpers or by adding a blank(ish) key and value pair to the end of the hash itself by modifying the product
in the controller after you load it into @product (like with a @product.add_blank_properties method). Otherwise you won't have 'blank' fields at the end of the loop. . .
I could go into more detail about adding a new blank line for a property after you create one using the previously existing blank line, but by the time you get this far you should have a good grasp on what you need to look for to solve that (and there are plenty of resources out there (you will probably be using ajax
for this).
Upvotes: 0
Reputation: 5664
The rails way needn't include the limitation of using a single table, ideally, you can do this in a very rails way with 2 tables. Moving on.
You shouldn't use Active Record :store
for this purpose in my opinion.
That implementation is ideal for situations where the developers need to store model metadata that is flexible in-code but well-defined at any given point of time. Which is to say, you need to specify keys in the model.
There is another pit-fall, you can't run SQL queries on the resulting serialized text that is saved.
If you insist, you can do this:
In your model:
class Product < ActiveRecord::Base
store :properties
def prop_hash
self.properties.collect{|k,v| [k,v]}
end
def prop_hash=(param_hash)
# need to ensure deleted values from form don't persist
self.properties.clear
param_hash.each do |name, value|
self.properties[name.to_sym] = value
end
end
end
In the view:
<%= form_for @product do |f| %>
<% f.object.prop_hash.each do |k,v| %>
<%= text_field 'product[prop_hash][][name]', k %>
<%= text_field 'product[prop_hash][][value]', v %>
<% end %>
<% end %>
Then you can also add an 'add another property' link which should use JS to insert another pair of inputs with the names product[prop_hash][][name]
and product[prop_hash][][value]
respectively.
Long ago I had rolled a custom implementation for metadata which saves the keys in serialized XML, for one reason - it can be queried in SQL. The link to my blog article http://geniitech.tumblr.com/post/14916592782/storing-metadata-as-xml-hash-in-ror
Upvotes: 13
Reputation: 7733
I think you should be using rails standard scaffold
but here you may need few customization's.
1) Need to declare resourceful route
for properties
property name
will become resource id
2) Index view
will iterate over properties hash
to populate relevant fields.
product.properties.each do |name, value|
puts name
puts value
end
2) Create
action should create a record if doesn't exist (based on name) already otherwise give an error
product.properties[:price] = 10
3) Update
will find property based on name and update it hash way
product.properties[:price] = 25
4) delete
will actually delete the key-value
pair from hash based on property name
product.properties.delete(:price)
Upvotes: -2