I want to be able to select a city from an existing array of cities.
I have stored the cities in an array in the ControllerName controller as a global variable
$cities = ["city1"...."city20"]
and I want to be able to access them via : <%= p.select(:city, $cities{ |c| [c.name, c.id]}) %>
but then I receive the error message undefined method name for 'city1'.
How can I select from that existing array?
Should I make some controller for cities?
Update #1
I came to make this code
$cities=['city1',...'city20']
#city = Array.new
i = $cities.size
i.times do |x|
#city[x] = City.new
#city << $cities[x]
end
and instead of undefined methodname' for "City1":StringI gotundefined method name' for "City20":String
PS : the table of cities has a column named 'name', so the problem isn't particularly there
Update #2 : Issue Solved
After reading a bit in this Rails Documentation I was able to make a little improvement on my code and I actually could solve the issue.
In my form , I have edited the code to that :
<%= p.select(:city, options_for_select( #cities_array ) )%>
And in my ControllerName Controller , I have put this :
$cities.length.times do |x|
#city = City.new({:name => $cities[x]})
#city.save
end
#cities_array = City.all.map { |city| [city.name, city.id] }
And that was all to it.
If you have any alternative solution , be it simpler or more complex, please share it as an answer.
You have multiple options to insert data into your database.
The first one. Add this on a migration:
rails generate migration add_cities_rows
inside your migration you can add something like:
cities = ['city1', 'city2', 'city3']
cities.each {|city| City.create(name: city)}
then you can run: rake db:migrate
You can add the same logic in db/seeds.rb file so if you want to regenerate your database running rake db:setup it will do the magic.
In your view(where you are using the select helper you can use the following sintaxis to fill the select:
<%= p.select :city, City.pluck(:name, :id) %>
So in your controller yo don't needto add any logic
This error is because 'city1' is a string and you are calling name method on a String object which isn't defined.
Probably you may have a City Model defined and must be having a database column **name within. So name method will be defined for a City object
Here, $cities is an array of strings which should be an array of City Objects instead.
Yes, make a controller for Cities and collect city objects array from these array of strings and pass on that to view
Related
I need to make a form that can handle the creation of multiple records of the same model, and submit the information in a way that passes through Rails’ Strong Parameters. On the front-end, I dynamically add two new text fields for puppy name and breed and want to save them all to a form. But I'm having trouble passing the array of puppy name and breed through Strong Params; getting
unknown attribute ‘puppies’ for Puppy
I think it's something very simple I'm overlooking. Please help generate these puppies!
new.html.erb
<%= form_tag puppies_path do %>
<%= text_field_tag “puppies[puppies][]puppy[name]” %>
<%= text_field_tag “puppies[puppies][]puppy[breed]” %>
<%= submit_tag “Add Puppy” %>
<% end %>
which generates these parameters
"puppies"=>{"puppies"=>[{"puppy"=>{"name"=>"ribbon", "breed"=>"poodle"}}]}
and the Strong Params in the controller
class PuppiesController < ApplicationController
def index
#puppies = Puppy.all
end
def new
end
def create
puppies_array = puppies_params[:puppies].each do |puppy|
puppy[:puppy]
end
Puppy.create(puppies_array)
end
def show
#puppy = Puppy.find(params[:id])
end
private
def puppies_params
params.require(:puppies).permit({
puppies: [
puppy: [
:name,
:breed
]
]
})
end
end
Any ideas what I’m missing?
Pseudo code of what I want to pass through:
[(name, breed), (name, breed), (name, breed)]
How can I write it correctly? I'm following this tutorial https://medium.com/#dmccoy/how-to-build-html-inputs-to-submit-an-array-of-hashes-with-a-rails-form-tag-that-work-with-strong-f70a6c03d78e
You haven't added rest of your controller code, but I am assuming you are using something like: Puppy.create(puppies_params)
But since you are nesting the params in your view, you will need to use Puppy.create(puppies_params[:puppies]) to ensure you are able to pass in the correct array of name and breed into the create action.
Edit
Since puppies array contains a hash with puppy key, we will need to extract those to get the final array of puppy attributes.
def create
puppies_array = puppies_params[:puppies].collect do |puppy|
puppy[:puppy]
end
Puppy.create(puppies_array)
end
My comment below has a shorthand for setting puppies array, you can use whichever version looks more readable and understandable to you
To get to the array of attributes that you want, you can do this:
puppies_array = puppies_params[:puppies][:puppy].map{|puppy| puppy.values}
This takes the hash puppies within puppy params, and takes the array of hashes in puppy and then takes just the value part of the hashes in that array.
You'll get your pseudo-code result.
For info, note that puppy in the map block has no special significance... it just indicates the instance of the array. You could just as easily do
puppies_array = puppies_params[:puppies][:puppy].map{|some_dog| some_dog.values}
I am totally new to Ruby on Rails, I was following some tutorials and developed a small CRUD application. In a form I have a drop down which was previously filled with hard coded values but now I have populated it with the values retrieved from database which works fine, but the trouble is I intend to get the selected value of the dropdown but instead of that I end up getting the id of that value, I know I am doing something logically wrong, I have did some research but was unable to come across a solution that fulfilled my requirement.
Here is my controller action,
def new
#list=CategoryType.all
end
Here is the dropdown within the form:
<%= f.label :maincategory %>
<% options = options_from_collection_for_select(#list, 'id','maincategory') %>
<%= f.select :maincategory, options %>
And this is the action getting back the parameters from the form in the controller:
private
def category_params
params.require(:category).permit(:name, :maincategory)
end
And this action saves it in the database:
def create
#category= Category.new(category_params)
if #category.save
redirect_to #category
else
render 'new'
end
end
Instead of maincategory id is being saved in the database, i am totally new to ROR, would really appreciate the help. Thanks in advance.
Why would you save the maincategory as a string ?
I suppose in your Category model, you should have a belongs_to :category_type.
I think it's better to save the CategoryType id of your maincategory object.
This way, you have the correct relation between your category and its type. Therefore, you could have the CategoryType of your category with this command : #category.category_type.name, if you want to retrieve the name of your record, as your data relation would be correct.
Let me know if I misunderstood something !
I'm really stuck in a issue.
I've a RoR application in which I'd like to populate select_tag with filtered options lets say Physicians. Here is my select_tag in view:
<%= select_tag "phyID",options_from_collection_for_select(#physicians,'id','fullNamePhy'),:include_blank => true %>
In controller I have
#physicians=User.find_by_userType('Physician')
but I'm getting error:
undefined method `map' for #<User:0x3b09820>
It seems like I have to use User.all instead of User.find. Please let me know any work around. Thanks in advance
This should work:
#physicians = User.where(userType: 'Physician')
You're getting error because options_from_collection_for_select expects object that behaves like Array, for example ActiveRecord::Relation instance. But find_by_* dynamic finder returns object representing singular record, User instance in this case.
BTW, column names in Rails are by convention named with underscore instead of camel case , like user_type.
You can use dynamic find_all_by finder
#physicians = User.find_all_by_userType('Physician')
find_by_column_names returns single record. Where as select_tag expects collection/array of record
For database columns that are integers that "represent" strings, what is the best way to show the string value in the view?
For example, if I collect "payment_method" values as integers in my form as follows:
<%= f.select :payment_method, { "Visa" => "1", "Mastercard" => "2", "Amex" => "3"} %>
How can I show the saved integer as a string in my view files? What can I add to my model, so that
<%= #relevantvariable.payment_method %>
or something similar returns string values like "Visa", "Mastercard" or "Amex" instead of their respective integer values?
Thanks much for handling such a basic question!
Either don't use an integer value, and store the payment method directly as a string in the db, or create a PaymentMethod model.
With the association set up, you'd be able to refer to the name of the payment method as so:
<%= #relevantvariable.payment_method.name %>
Don't try to manually handle lists of names / ids - that will quickly get unmanageable.
Edit: after reading your comment, if you went with the first option and stored a string in the table, definitely don't allow the user to type the value directly, maintain a list on the model (or similar), that you seed your dropdown from, that way you're still constraining the possible values. You could even add a custom validator if you want to be certain what you putting in the database.
I'd stick with cheeseweasel solution but you can do one thing to show that on your view...
<% if #relevantvariable.payment_method == 1 %>
<%= "Visa" %>
<% else %>
<%= "Mastercard" %>
You probably would want to use a switch/case instead but you got the idea
As I said I think you should stick with cheeseweasel solution since there are many problems with this approach... it's your call
So you have your payment methods in a separate table payment_methods and the owner ( say user) contains a has_one relationship with it
class User < AR::Base
has_one :payment_method
end
Then show the payment method name just like
<%=
#user.payment_method.name #or whatever you have.
%>
However, while you are loading the #user data, you can perform a eager loading by :include. Like
User.find(user_id, :include => :payment_method)
This will load the PaymentMethod data earlier even for multiple users with single query.
I have a pretty standard use-case. I have a parent object and a list of child objects. I want to have a tabular form where I can edit all the children at once, as rows in the table. I also want to be able to insert one or more new rows, and on submit have them be created as new records.
When I use a fields_for to render a series of sub-forms for nested records related by has-many, rails generates field names e.g. parent[children_attributes][0][fieldname], parent[children_attributes][1][fieldname] and so on.
This causes Rack to parse a params hash that looks like:
{ "parent" => {
"children" => {
"0" => { ... },
"1" => { ... } } }
When passed a new (un-persisted) object, the same fields_for will generate a field name that looks like:
parent[children_attributes][][fieldname]
Note the [] with no index in it.
This cannot be posted in the same form with the fields containing [0], [1], etc. because Rack gets confused and raises
TypeError: expected Array (got Rack::Utils::KeySpaceConstrainedParams)
"OK", thinks I. "I'll just make sure all the fields use the [] form instead of the [index] form. But I can't figure out how to convince fields_for to do this consistently. Even if I give it an explicit field name prefix and object:
fields_for 'parent[children_attributes][]', child do |f| ...
So long as child is persisted, it will automatically modify the fieldnames so that they become e.g. parent[children_attributes][0][fieldname], while leaving fieldnames for new records as parent[children_attributes][][fieldname]. Once again, Rack barfs.
I'm at a loss. How the heck do I use standard Rails helpers like fields_for to submit multiple new records, along with existing records, have them be parsed as an array in the params, and have all the records lacking IDs be created as new records in the DB? Am I out of luck and I just have to generate all the field names manually?
As others have mentioned, the [] should contain a key for new records because otherwise it is mixing a hash with an array type. You can set this with the child_index option on fields_for.
f.fields_for :items, Item.new, child_index: "NEW_ITEM" # ...
I usually do this using the object_id instead to ensure it is unique in case there are multiple new items.
item = Item.new
f.fields_for :items, item, child_index: item.object_id # ...
Here's an abstract helper method that does this. This assumes there is a partial with the name of item_fields which it will render.
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
You can use it like this. The arguments are: the name of the link, the parent's form builder, and the name of the association on the parent model.
<%= link_to_add_fields "Add Item", f, :items %>
And here is some CoffeeScript to listen to the click event of that link, insert the fields, and update the object id with the current time to give it a unique key.
jQuery ->
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
That code is taken from this RailsCasts Pro episode which requires a paid subscription. However, there is a full working example freely available on GitHub.
Update: I want to point out that inserting a child_index placeholder is not always necessary. If you do not want to use JavaScript to insert new records dynamically, you can build them up ahead of time:
def new
#project = Project.new
3.times { #project.items.build }
end
<%= f.fields_for :items do |builder| %>
Rails will automatically insert an index for the new records so it should just work.
So, I was not happy with the solution I saw most often, which was to generate a pseudo-index for new elements, either on the server or in client-side JS. This feels like a kludge, especially in light of the fact that Rails/Rack is perfectly capable of parsing lists of items so long as they all use empty brackets ([]) as the index. Here's an approximation of the code I wound up with:
# note that this is NOT f.fields_for.
fields_for 'parent[children_attributes][]', child, index: nil do |f|
f.label :name
f.text_field :name
# ...
end
Ending the field name prefix with [], coupled with the index: nil option, disables the index generation Rails so helpfully tries to provide for persisted objects. This snippet works for both new and saved objects. The resulting form parameters, since they consistently use [], are parsed into an array in the params:
params[:parent][:children_attributes] # => [{"name" => "..."}, {...}]
The Parent#children_attributes= method generated by accepts_nested_attributes_for :children deals with this array just fine, updating changed records, adding new ones (ones lacking an "id" key), and removing the ones with the "_destroy" key set.
I'm still bothered that Rails makes this so difficult, and that I had to revert to a hardcoded field name prefix string instead of using e.g. f.fields_for :children, index: nil. For the record, even doing the following:
f.fields_for :children, index: nil, child_index: nil do |f| ...
...fails to disable field index generation.
I'm considering writing a Rails patch to make this easier, but I don't know if enough people care or if it would even be accepted.
EDIT: User #Macario has clued me in to why Rails prefers explicit indices in field names: once you get into three layers of nested models, there needs to be a way to discriminate which second-level model a third-level attribute belongs to.
The common solution is to add a placeholder into [], and replace it with a unique number on inserting the snippet to the form. Timestamp works most of the time.
Maybe you should just cheat. Put the new records in a different faux attribute that is a decorator for the actual one.
parent[children_attributes][0][fieldname]
parent[new_children_attributes][][fieldname]
It's not pretty, but it should work. It might take some extra effort to support round-trips to the form for validation errors.
I've came across this user case in all my last proyects, and I expect this to continue, as julian7 pointed, it is necesary to provide a unique id inside the []. In my opinion this is better done via js. I've been dragging and improving a jquery plugin for dealing with this situations. It works with existing records and for adding new records but expects a certain markup and it degrades gracefully, heres the code and an example:
https://gist.github.com/3096634
Caveats for using the plugin:
The fields_for call should be wrapped in a <fieldset> with data-association attribute equal to the pluralized name of the model, and a class 'nested_models'.
an object should be built in the view just before calling fields_for.
the object fields perse should be wrapped in a <fieldset> with class "new" but only if the record is new (cant remember if I removed this requirement).
A checkbox for the '_destroy' attribute inside a label must exist, the plugin will use the label text to create a destroy link.
A link with class 'add_record' should exist within the fieldset.nested_models but outside the fieldset enclosing the model fields.
Appart from this nuisances its been working wonders for me.
After checking the gist this requirements must be clearer.
Please let me know if you improve on the code or if you use it :).
BTW, I was inspired by Ryan Bates first nested models screencast.
long post deleted
Ryan has an episode on this:
http://railscasts.com/episodes/196-nested-model-form-revised
It looks like you need to generate the unique index manually. Ryan uses the object_id for this.
I think you can make it work by including the id of the record as a hidden field
There is a gem called cocoon for doing this, I would go for a leaner mor DIY aproach but it was specifically built for this cases.