In the Rails Tutorial there is a great chapter on creating a toy app with users and microposts. However, when editing the microposts, I can only edit user_id which is a numeric value, not user name. Is there a simple way to enforce displaying user's name instead of user's id in the app?
I've looked app/views/microposts/_form.html.erb and it says:
<div class="field">
<%= f.label :user_id %><br>
<%= f.number_field :user_id %>
</div>
What should I change to be able to select the users by name instead of the id?
Try using a select helper rather than a number_field.
<%= f.collection_select(:user_id, #users, :id, :first_name) %>
In your controller, you'd need the following line (or something similar):
#users = User.all
If you want to display each user's full name, you'd need to create a method in user.rb to concatenate first and last names, like so:
def fullname
fullname = "#{last_name}, #{first_name}"
end
Your select would then use the method name, like this:
<%= f.collection_select(:user_id, #users, :id, :fullname) %>
You should probably take some time to read up on all the different form helpers.
The feature you're looking for is called a collection_select. http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select
f.collection_select :user_id, User.all, :id, :username
Related
I have a controller named Welcome with view called index.
In my index view i have created a small form as such.
<%= form_for :location do |f| %>
<%= f.label :Longitude %><br>
<%= f.text_field :integer %>
<br>
<br>
<%= f.label :Latitude %><br>
<%= f.text_field :integer %>
<p>
<%= f.submit %>
</p>
<% end %>
In this form the user can enter some integer value for longitude and latitude. Once the user enters value for longitude and latitude. They click submit. Upon submit i would like to store these values in my controller. So i am using the following method where i have two instance variables taking values from the form.
def index
#long = params[:longitude]
#lat = params[:latitude]
end
In my routes.rb I have
get 'welcome/index'
post 'welcome/index'
Please tell me where i went wrong. Also if someone can suggest a better way of doing this also i would appreciate it i am new to rails and i want to learn the correct way of doing things so i don't create bad habits early on.
The reason it's not working is because your fields are both named :integer, and since they share the same name, the browser will only send one value.
So, with your code, if you filled in the first field with 'a' and the second with 'b', your params would contain something like this:
{ location: { integer: "aaa" } }
Which obviously isn't what you want! If your HTML looked more like this (I've stripped the layout stuff to make things clearer):
<%= form_for :location do |f| %>
<%= f.label :longitude %>
<%= f.text_field :longitude %>
<%= f.label :latitude %>
<%= f.text_field :latitude %>
<%= f.submit %>
<% end %>
Then you could access the params in your controller params[:location][:longitude] and params[:location][:latitude]
A good idea to see the difference between the effect of your form vs this form would be to inspect the html. Take a look at the input name attributes, and label for attributes and see how they match up with the params Rails receives. Also, when you post the form, be sure to look in your server log to see the params! :)
After reading your question, I think you want to see how controllers, views and models work. For learning purpose you can generate scaffold and study the generated code.
For example, generate a model GeoLocation, related controller and views by this:
rails g scaffold GeoLocation longitude:string latitude:string
Now fire up rails server and browse http://localhost:3000/geo_locations/new and save your long, lat. I wrote this answer to give you some guidance.
You can follow these excellent books:
The book of Ruby
The Rails 4 Way
The Attraction model belongs to Destination.
I'm trying to create a new attraction (text_field :name) but also linking it to an already-created destination. These destinations are rendered in a drop-down menu with this collection_select tag. By clicking submit, I'm want the attraction to be created and saved in the activerecord database with the Destinations foreign key.
f.collection_select(:attraction, :destination_id, Destination.all, :id, :name) %>
The whole block looks like this at the moment:
<h1>New attraction</h1>
Select a City
<%= f.collection_select(:attraction, :destination_id, Destination.all, :id, :name) %>
<div class ="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
How can I save the attraction to the database with the appropriate destinaton? Thanks in advance!!
I haven't use f.collection_select() so not sure what it is called inside the params, but lets assume for the following code that it's params[:attraction][:destination_id]. You can change your create action to:
def create
#destination = Destination.find(params[:attraction][:destination_id])
#attraction = #destination.attractions.build(params[:attraction])
if #attraction.save
...
else
...
end
end
That's assuming that you've created a has_many and belongs_to association.
If you run into a mass-assignment error then change the first line to:
#destination = Destination.find(params[:attraction].delete(:destination_id))
# this will return the deleted value, and in the next line of code
# you'll have the rest of the params still available
Here is the problem I'm having, and I have tried thinking around, but still can't figure out the solution. So I have two models
User
Data
and Experience belongs to user, and accepts nested attributes
Now here comes the problem ! I have page/form where I would like to update or insert.
so in the Data model
flavor, body
So How do I add form tag where I can specify my flavor but let user decide the body so for example, currently I have
<%= f.text_field :body, placeholder: "...." %>
So how do I do something like (wrong syntax)
<%= f.text_field :body, :flavor => "someflav" , placeholder: "...." %>
<%= f.text_field :body, :flavor => "Otherflav" , placeholder: "...." %>
and so on...
How does one achieve this ? I have looked around rails api, and but couldn't figure out how to achieve my issue.
Thanks for your consideration and time.
You need to use fields_for
Rails constructs input names that help it determine exactly what attribute goes where.
For instance:
user[datas_attributes][0][body]
Since (if I am interpreting you correctly) User has many Datas, it would look something like this:
<%= fields_for :datas do |data_fields| %>
<%= data_fields.text_field :body %>
<% end %>
There are a few things you need to do to make this work.
In your model, you need to add the following two lines:
accepts_nested_attributes_for :datas
attr_accessible :datas_attributes
I have a software table, which has 4 fields; id, vendor, title and edition. I need a drop down box in my licenses form which will show every record like this: vendor - title - edition, but then only save the id of the chosen record to the database. I'm currently using a text box where the user can just enter the id of the software which will be saved to the database. here is my current form:
<%= form_for(#licenses) do |f| %>
<div class="well">
<%= f.label 'Software' %><br />
<%= f.text_field :software_id %>
<%= f.label 'Quantity' %><br />
<%= f.text_field :amount %>
<%= f.submit 'add'%>
</div>
<% end %>
I need to change the software text field into a drop down box, sorry if this is vague, i've not had anything to do with drop down boxes before.
Checkout collection_select for populating drop box
create a method in softwarer model like
def title_edition
"#{self.vendor.name}- #{self.title} - #{edition}"
end
#softwares = Software.all #In controller
and in view
<%= f.collection_select :software_id ,#softwares,:id,:title_edition %>
You would want collection_select with something like this:
f.collection_select(:software_id, Software.all, :id, :blah, :prompt => true)
Now the :blah is the tricky thing. In your Software model you will need to define a method that returns the concatenated string you would like; I called it :blah to draw your attention to it but it can be named anything. It would look like this:
def blah
"#{self.vendor}-#{self.title}-#{self.edition}"
end
That should return the string like you mentioned and when you call it in collection_select display it like you want it.
I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.
But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.
If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.
In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:
<%= simple_form_for #user do |f| %>
<%= f.input :agreement, as: :fake %>
....
Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.
UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
end
end
You can use attr_accessor
class Order < ActiveRecord::Base
attr_accessor :card_number
end
Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.
See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor
and here for a useful stackoverflow question What is attr_accessor in Ruby?
Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible
The best way to handle this is to use simple_fields_for like so:
<%= simple_form_for #user do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= simple_fields_for :other do |o| %>
<%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
<% end %>
<% end %>
In this example, I have added a new field called change_password which is not part of the underlying user model.
The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by #baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.
Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.
I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...
Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Example:
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
<%= devise_error_messages! %>
<% resource.class.invite_key_fields.each do |field| -%>
<%= f.input field %>
<%= hidden_field_tag :object_name, #object.class.name %>
<%= hidden_field_tag :object_id, #object.id %>
<% end -%>
I found a very simple (and somewhat strange) workaround.
Just add the input_html option with any value key inside. E.g:
= simple_form_for #user do |f|
= f.input :whatever, input_html: {value: ''}
Tested simple_from versions: 3.2.1, 3.5.1