I'm working on an app where I have a user preference to show distances or weights in metric or standard units. I'm using a filter to save the units in a consistent format in the database
before_save :convert_distance
What's the best way to display these units in my views? I tried adding a getter in my model called display_distance like below, but it didn't work.
#model
def display_distance
self.workout.user.uses_metric ? self.distance * 1.609344 : self.distance
end
#view
<%= f.text_field :distance, {:value=>f.object.display_distance} %>
#error
You have a nil object when you didn't expect it!
I did a to_yaml and verified that f.object is an instance of the correct object. Also, I'm using nested forms, I'm not sure if that matters.
Also, I'm not sure if I should be trying to do the conversion in the model or in a helper. I thought the model would be better as I'll be using this functionality in multiple views.
The form does not know anything about your object, you have to pass it in through your controller. You likely have something like this in your controller (assuming this is the edit view):
def edit
#my_object = MyObject.find(params[:id])
end
Then you can access this in the view like so:
<%= #my_object.display_distance %>
Related
I've got a Rails 4 app with a service that loads required objects on a new/edit document action, and this is a method from it:
def template_variables
if #document.template_variables.any?
TemplateVariable.where(id: document_vars).each do |v|
next unless User.method_defined?(v.name.to_sym)
v.update_attribute(:text, #user.send(v.name.to_sym)) # problem line, persists the change when i don't want to
v.text = #user.send(v.name.to_sym) # also a problem, doesn't update the value in the form at all
end
else
TemplateVariable.where(id: master_vars)
end
end
I need a solution for the two problem lines (they are just two things I've thought of, and they aren't supposed to both be there but I've included both for the sake of my problem).
The first updates and persists the change to the model which is behaviour I don't want. The second line doesn't do anything, where logically it seems like it should replace whatever text was in that variable in the form with #user.send(v.name.to_sym). It appears to do nothing.
Is there are solution to this problem that I'm unaware of?
Bonus points if there's a way to list the fields with new values to display in a flash[:notice].
Update now with relevant form code.
<%= v.input :text, as: :string, input_html: { value: v.object.text } %>
Setting the value/vs not setting it doesn't change anything either.
If you've got an instance variable that you're passing from your controller, you can set (but not save) values on that variable which will be available in the view.
For example, you can set the first and last name of a new user by passing in arguments or setting the attributes in the controller:
#UsersController
def new
#user = User.new(first_name: 'John')
#user.last_name = 'Smith'
end
In the view
#users/new.erb.html
<%= #user.first_name # will be John %>
<%= #user.last_name # will be Smith %>
But in your case, you're using update_attribute, which saves the record changes. Instead, you should be creating an instance variable, setting (but not saving) the values on that, and using that in the view/form
e.g.
#varibales = TemplateVariable.where(id: document_vars)
#variables.each do |variable|
#change what you want here
variable.foo = 'bar'
end
And then reference your #variables object in the view/form.
In saying all of that, you should strive to only use one instance variable in your controller, consider using form objects, if you need to pass multiple values from the controller to the view.
As for the flash notice, you can display whatever you like as a flash notice by setting that in the controller, assuming you've got your view setup to display flash notices as shown here
flash[:notice] = "The value of #{#variables.first.foo}"
I know I've written it wrong, but I'm looking at the documentation and can't figure out how.
My model is Quote and has three fields, body, attribution, and work, all strings. The form is intended to add a new quote to a page of quotations.
on main/index.html.erb
<%= form_for(:quote, url: {action: 'create'}) do |f| %>
<%= f.text_field :body %>
<%= f.text_field :attribution %>
<%= f.text_field :work %>
<%= submit_tag "Submit" %>
<% end %>
in main_controller.rb
def create
Quote.create(body: params[:body], attribution: params[:attribution], work: params[:work])
end
The form submits, and an entry is saved to the database -- but it's a totally blank entry. I'm not sure why. Help would be appreciated!
Three things:
The way rails forms are supposed to work, you're not meant to get body, attribution, etc independently, they should be wrapped up into a quote object. But...
In your form, your not properly binding an object to the form the way rails expects. You can read more in the documentation here: http://guides.rubyonrails.org/form_helpers.html#binding-a-form-to-an-object. You could also generate a fake scaffold rails generate scaffold HighScore game:string score:integer to generate a fake model and see an example of how it's supposed to work. The default scaffolding even has simple examples of how to deal with save errors.
Finally, as #Paven suggested, when you get confused, be sure to look at what's going on in your log - i.e. what params are being posted to your create action. That is always helpful and a good way to diagnose problems quickly.
Your form does't need the action argument. The form_for helper uses ActiveRecord objects to determine the path, meaning as long as you build your object correctly, you won't need to determine your path individually:
<%= form_for #quote do |f| %>
Secondly, you'll want to look at your create method:
#app/controllers/quotes_controller.rb
def new
#quote = Quote.new
end
def create
#quote = Quote.new(quote_params)
end
private
def quote_params
params.require(:quote).permit(:body, :attribution, :work)
end
The problem is you're not sending an ActiveRecord object to your form_for helper. You can read the explanation here:
In Rails, this is usually achieved by creating the form using form_for
and a number of related helper methods. form_for generates an
appropriate form tag and yields a form builder object that knows the
model the form is about. Input fields are created by calling methods
defined on the form builder, which means they are able to generate the
appropriate names and default values corresponding to the model
attributes, as well as convenient IDs, etc. Conventions in the
generated field names allow controllers to receive form data nicely
structured in params with no effort on your side.
In order to get the form working correctly, you need to be able to provide a valid ActiveRecord object (#variable), which the helper can use to determine the url etc
My code above helps you provide a new ActiveRecord variable, and allows you to use it in the form. This should allow the form_for method to send your data to the create method, which will then create & save an object in the db for you
Newbie to RoR. I can't grasp the concept of how to call a simple method on my form controller from my view. I want to collect 2 pieces of information form the view, call a method on the form controller that will retrieve a piece of information based on the parameters, and then display the piece of information on that same view or another one. Right now, I was trying to use a form controller instance variable to accomplish the displaying of the new piece of information--not sure how I will refresh the view to display it but that is a future hurdle. Right now, I can click my Submit button without getting any errors but it is clearly not hitting my form controller method.
Here is my erb file for the view:
<h1>Enter Required Information</h1>
<%= form_tag (get_hotel_recommendation_path) do %>
<%= label_tag(:name, "Name:") %>
<%= text_field_tag(:name) %>
<%= label_tag(:date, "Date (yyyy-mm-dd):") %>
<%= text_field_tag(:date) %>
<%= submit_tag("Submit") %><br><br>
<%= label_tag(:recommendation, "Recommendation:") %>
<%= #recommended_hotel_name %>
<% end %>
Here is my form controller code:
class RecommendHotelController < ApplicationController
#recommended_hotel_name = ''
def collect_info
end
def get_hotel_recommendation
#recommended_hotel_name = Member.recommended_hotel_name( params[:name], params[:date] )
end
end
I really just want a simple and easy way to do this--not necessarily the best. I just need a quick UI to demonstrate my model code. And I need to get it done soon.
New form controller code:
class RecommendHotelController < ApplicationController
def collect_info
end
def get_hotel_recommendation
redirect_to recommend_hotel_path
end
protected
helper_method :recommended_hotel_name
def recommended_hotel_name
unless (params[:name].nil?)
Member.recommended_hotel_name(params[:name], params[:date])
end
end
end
Once the controller hands over to the view, there's no going back to call additional methods. It is the controller's job to prepare everything the view might need in advance. Once inside the render phase, there's no way to call controller methods.
The exception to this is helper methods which can be called. You need to declare any methods you want to use within your view explicitly. As an example:
helper_method :get_hotel_recommendation
def get_hotel_recommendation
# ...
end
Helper methods can also be located inside the associated helper module, and it's a good idea to put them there if they're used exclusively within views.
In your case, if you're using this only once, you should probably skip the assignment to an instance variable and just return the object in question. The view would look like:
<%= recommended_hotel_name %>
The adjusted controller method:
class RecommendHotelController < ApplicationController
protected
helper_method :recommended_hotel_name
def recommended_hotel_name
Member.recommended_hotel_name(params[:name], params[:date])
end
end
It's worth noting that declaring #recommended_hotel_name = '' in the class context is probably not what you intend. This creates a class variable, not an instance variable. Instance variables in controllers must be defined inside the primary action method or inside a before_filter method. Also remember that instance variables are nil by default, so there's no need to initialize them to that first. An empty string and nil are equivalent when used within a view, everything inside <%= ... %> is converted to a string for you automatically.
Another thing to watch out for is leaving a space between a method name and its arguments. It should be form_tag(...) and not form_tag (...). Normally this does not make a difference, Ruby can be very lenient, but sometimes it can subtly alter the way the arguments are interpreted leading to a lot of confusion as you try to diagnose the problem. Stylistically speaking, only keywords like if, while and case have a space before the brackets as these are not method calls.
As to why your form isn't working, it's not clear. Those parameters should be submitted as you intend, but maybe you're not getting the right routing. Remember it's best to stick with the standard index, new, show, and edit names unless you're doing something exotic. In this case, you should probably define this as index if it shows more than one record or show if it's always one record.
You can get all the information from a form submit in the params array. However, from your example, I would advise to use: form_for instead of using separate form_tags.
I found a gazillion answers for my issue if I was using Mongo, but none of the ones I see work out here since I am not using mongo.
Basically I have a report_controller.rb that has a very simple method defined:
def donations_by_season
#donations = Donation
end
and a very simple report/donations_by_season.html.erb as follows:
<%= form_for #donations do |f| %>
Stuff Will go here... such as fields to select a date for the season we wish to view.
<% end %>
There is no report model, just a controller and views.
But when I attempt to view /reports/donations_by_season
I immediately get:
undefined method to_key' for #<Class:0x00000114d85918>
What should I do to fix that? Am I doing my form incorrectly since there is no model associated with reports?
You should never be assigning an instance variable to point to a class object like this. You probably want this:
def donations_by_season
#donations = Donation.all
end
Note the .all versus just leaving it blank. You could also do .new or a litany of other methods, depending on what you're trying to do.
I have the following code in a layout:
Posted <%=time_ago_in_words post.created_at %> ago
<% if post.has_tag != nil %>
in the <%= post.get_first_tag.name %> category
<% end %>
And the following code in the post model which is inheriting form ActiveRecord::Base
def has_tag
!self.tags.empty?
end
def get_first_tag
self.tags[0]
end
Tags is also inherited from ActiveRecord::Base and Post 'has_many' Tags
Firstly: Is this the best way of checking if the post object has at least 1 associate tag attribute.
Secondly: Should I be putting this logic into a helper method?
Thirdly: Why doesn't the following work (it returns a # where the tags should be):
in the <%= post.tags.to_sentence %> category,
I guess its because tags aren't actually stored as an array attribute, but i don't really know.
This is a perfectly good way of checking if there are tags or not. However, self.tags.empty? will return true or false so post.has_tag will never be nil.
It's worth noting that, in ruby, it is common to name methods that return true or false with a question mark. So post.has_tag? would be a better name for your method (like the empty? method for the tags).
This sort of method belongs in the model class rather than a helper as it is not specific to the view layer; you might want to call this method from other model classes, for example.
The reason you are getting # instead of your tag names is that you are trying to convert a collection of tags to a sentence and you need instead to convert the names of the tags to a sentence. You should be able to do
post.tags.map(&:name).to_sentence
which will take the names of the tags and turn them into a sentence.
For one thing, you probably need
<% if post.has_tag %>
instead of
<% if post.has_tag != nil %>
In your definition, has_tag should never return nil, and thus 'in the...' part will always be shown.
Generally, your idea seems fine to me: I often add helpers like these to models.