add comparison feature in rails - html

i'm having a bit of trouble with adding a certain feature. i'm working on a buy/sell site and i want to be able to compare posts. here's what i have so far:
in the posts view:
<%= button_to "Add to Compare", :action => "addCompare" %>
in the corresponding controller:
##a = Array.new()
def addCompare
##a << Post.id
end
so, all i want to do is add the post's id to the array ##a. when i test this, i click on the "Add to Compare" button and I'm welcomed with this:
Template is missing
Missing template posts/addCompare with {:locale=>[:en, :en], :formats=>[:html], :handlers=>[:rxml, :rjs, :builder, :rhtml, :erb]} in view paths "/home/mja32/470repo/traders/app/views", "/var/lib/gems/1.8/gems/devise-1.4.2/app/views"
So I guess it's trying to redirect to a view. How do I prevent it from doing this? All I want this button to do is to add the post's id to the array and nothing more.
Thanks in advance,
Matt

First of all, storing persistent data in a controller's class variable isn't going to work the way you want it to. There's no guarantee that ##a will be the same array on your next addCompare call; for example, your next addCompare call could be handled by a different process. Also, what happens if two different clients call addCompare? Do you really want to mix their data together in one pile? Probably not. Your first task is to replace ##a with a real per-user persistent store.
If you want to return nothing at all from your controller, just do this at the end of your controller method:
render :nothing => true, :status => :ok
That will tell Rails that something has already been rendered so it doesn't need to try the default rendering action (which is to render the posts/addCompare view) and returns nothing more than a 200 status code to the client.
Once that's in place, you'll probably want to AJAXify your button with :remote => true:
:remote - If set to true, will allow the Unobtrusive JavaScript drivers to control the submit behaviour. By default this behaviour is an ajax submit.
So this:
<%= button_to "Add to Compare", { :action => "addCompare" }, { :remote => true } %>
Note that button_to looks like this:
button_to(name, options = {}, html_options = {})
and that :action is for options but :remote is for html_options so you have to explicitly set up the hashes with {}; you could just wrap the options in braces:
<%= button_to "Add to Compare", { :action => "addCompare" }, :remote => true %>
but I prefer the consistency of wrapping them both by hand.

Related

Submitting an empty text field as an empty string, not null, in a multipart form

This is happening in my Rails app, but I'm not sure whether this is an issue with Rails or if I'm misunderstanding how multipart forms are supposed to work.
Here's (something like) my form:
<%= form_for #user do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
User#name has a presence validation. If I visit users/1/edit, empty out the value of the 'name' text field, and click submit, nothing gets updated because the presence validation fails. So far, so good.
But now I've added an avatar attribute to my User (using the Paperclip gem for file storage), so I update the form accordingly:
<%= form_for #user do |f| %>
<%= f.text_field :name %>
<%= f.file_field :avatar %>
<%= f.submit %>
<% end %>
Now, if I go to edit the user and submit a blank value for name, the validation doesn't fail. User#name keeps it previous value, but the update still appears to be successful (i.e. I don't get an error message about failed validations and the updated_at timestamp in the database gets updated.)
On closer inspection, it seems that when I include a file_field in the form, it changes the form's behaviour when submitting blank text fields (probably due to the fact that form_for now outputs a form with enctype=-"multipart/form-data").
When the file_field isn't present, submitting a blank name sends these params to the server:
{ "id" => 1, "user" => { "name: "" }
Which results in something like User.find(1).update_attributes(name: "") in the controller, which of course fails to update because Rails sees that we're trying to update 'name' to a blank string and the validation fails.
When it is present, this gets submitted:
{ "id" => 1, "user" => { } (plus extra info about the avatar file)
The "name" key isn't present at all, so the controller runs User.find(1).update_attributes() which of course passes as there's nothing being updated that might fail a validation.
Is this a bug, or a feature? Why would changing the enctype to multipart (assuming that's the source of the problem) change the way blank text fields behave? (I've tested this in both Chrome and FF, fwiw.) If this is really the intended behaviour, how can I ensure that blank text fields get submitted properly without having to add a bunch of tedious boilerplate around every text field?
(If it matters, I'm using: Ruby 2.3.0, Rails 5.0.0.beta3, Paperclip 5.0.0.beta1, and I've tested in Chrome 49 and Firefox 45)
I had the same issue, updating Rails to the latest version fixed it.
I think you can do something like that
updated_user = User.new(your_params) # The new attributes is set to a new object record
target_user = User.find(params[:id]) # the user you want to update
target_user = updated_user
if target_user.save?
..
else
..
by this all the validations will be triggered and the active record will have all the new attributes, if the name is blank it'll catch it
The following strong param method won't bring back the dropped :name parameter, but it should prevent the form from imitating a successful submit:
private
def user_params
params.require(:user).permit(:name, :avatar)
end
This can be inserted as a private method under your Users Controller. Then simply call user_params in your Update method.

.collect method get an error undefined method `collect' for nil:NilClass

I'm trying to have a drop down list but when i try it it give me
undefined method `collect' for nil:NilClass
the controller:
def existing
#courses = Course.all
end
def duplicate
course = Course.find_by_id(permitd_up[:id])
new_course = course.dup
if new_course.save
redirect_to :action => 'show'
else
redirect_to :back
end
end
the view:
<h3>Choose a Course</h3>
<%= form_for :course , url: {:action => "duplicate" , method: "post"} do |f|%>
<%= f.select :id , #courses.collect{|c| [c.id , c.name]} %>
<br><br>
<%= f.submit%>
<%end%>
You will receive the following error
undefined method `collect' for nil:NilClass
on
<%= f.select :id , #courses.collect{|c| [c.id , c.name]} %>
Only when #courses instance variable was not set in the action that rendered this particular view.
I see that #courses variable is set in the existing method. If you are using existing as an action which renders this view then your view name must be existing.html.erb.
Or if you are rendering the view from a different action then in that case you should set #courses value in that particular action by either directly setting the value within action OR by calling existing method from there.
If you have your courses as a database table, you might want to try using rails' built in field helper collection_select. It will populate your select field with all of the data available in your model. If you want a drop-down like the one you are describing, I believe using collection select is the best way to handle it.
You can read up on it here: http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html
Alternatively, if you have a ton of courses, maybe try looking into using a text field with autocomplete. Jquery UI has a plugin that makes this very easy. You can check out the railscasts for it here: http://railscasts.com/episodes/102-auto-complete-association-revised.
It requires a pro account but if you do a lot of rails developing it will be the best $9 you spend every month.
If you would like to continue to do it this way, make sure that you are defining
#courses = Courses(:all) in the correct controller action, otherwise you will have nothing to render.

How to Fix Rails Code when Moving it From Controller to Helpers?

On each text_area on my site, I have a select_box for selecting the language. It often uses certain default languages, but sometimes checks for custom options. I initially had the array setup in the controller:
#language_array = [ ["english", 1], ["french", 2], ["spanish", 3] ]
#language_array = get_custom_array if custom_language?
And it would then be accessed by the view:
<%= select_tag(:language, options_for_select(language_array, default_language) )%>
This worked fine, and the text_area would display the language and pass on the number. However, I would now like to add multiple text_areas to each page, so I moved the code into a helper, and access the helper from every text_area in the view:
language_array = get_language_array(thing.id)
<%= select_tag(:language, options_for_select(language_array, default_language) )%>
However, now the text_area comes out messed up, and displays the array instead of just the language:
["english", 1]
How can I fix it (without changing the currently stored arrays)? I also want variables from the helper to be available to javascript on the page. Is there a better way to deal with multiple "things" than to move everything from the controller to the helpers?
You could have them inside the model as a hash like this
user.rb
def User
def self.language
{
'English' => '1',
'French' => '2',
....
}
end
end
and in the controller obtain the hash into a variable and it will be accessible in both the html.erb and the js.erb
users_controller.rb
#language = User.language
Then while building the form you can do it as
<%= talent_field.select(:language, #language, {}, {:class => 'language-select'} %>

Keeping form data in Rails

I have an HTML form in Rails. Sometimes when the user submit data, my server will reject it because the data is not appropriate (say, the user types a non-number in a field where there's supposed to be only numbers.)
<%= form_tag "myproject/win", :id => "win_form" do -%>
...
<label>
Year
<%= text_field_tag "year", '', :size => 20 %>
</label>
...
<%= submit_tag "Go!" %>
<% end -%>
In that case, my controller returns an error message
#notice = "Year must be a number"
return render action: "new"
But all the data that the user typed in disappears, and the user has to restart with a blank form. Is there a way to keep that data for the user?
That's fine if you don't have a data store behind this, but you can still leverage ActiveModel to create an object that you can use to help build forms (or use the reform gem). If you really don't want to do that, despite the additional capabilities it affords you, you can just use what you have now, but pay attention to the arguments:
From http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-text_field_tag:
text_field_tag(name, value = nil, options = {})
Creates a standard text field; use these text fields to input smaller chunks of text like a username or a search query.
Your code:
text_field_tag "year", '', :size => 20
So you're passing in '' as the value, and asking us why the value is ''. Perhaps what you really want is this:
text_field_tag "year", params[:year], :size => 20

What's the right way to define an anchor tag in rails?

It's obvious from the documentation (and google) how to generate a link with a segment e.g. podcast/5#comments. You just pass a value for :anchor to link_to.
My concern is about the much simpler task of generating the <a name="comments">Comments</a> tag i.e. the destination of the first link.
I've tried the following, and although they seemed to work, the markup was not what I expected:
link_to "Comments", :name => "comments"
link_to "Comments", :anchor => "comments"
I think I'm missing something obvious. Thanks.
You are getting confused by Ruby's syntactic sugar (which Rails uses profusely). Let me explain this briefly before answering your question.
When a ruby function takes a single parameter that is a hash:
def foo(options)
#options is a hash with parameters inside
end
You can 'forget' to put the parenthesis/brackets, and call it like this:
foo :param => value, :param2 => value
Ruby will fill out the blanks and understand that what you are trying to accomplish is this:
foo({:param => value, :param2 => value})
Now, to your question: link_to takes two optional hashes - one is called options and the other html_options. You can imagine it defined like this (this is an approximation, it is much more complex)
def link_to(name, options, html_options)
...
end
Now, if you invoke it this way:
link_to 'Comments', :name => 'Comments'
Ruby will get a little confused. It will try to "fill out the blanks" for you, but incorrectly:
link_to('Comments', {:name => 'Comments'}, {}) # incorrect
It will think that name => 'Comments' part belongs to options, not to html_options!
You have to help ruby by filling up the blanks yourself. Put all the parenthesis in place and it will behave as expected:
link_to('Comments', {}, {:name => 'Comments'}) # correct
You can actually remove the last set of brackets if you want:
link_to("Comments", {}, :name => "comments") # also correct
In order to use html_options, you must leave the first set of brackets, though. For example, you will need to do this for a link with confirmation message and name:
link_to("Comments", {:confirm => 'Sure?'}, :name => "comments")
Other rails helpers have a similar construction (i.e. form_for, collection_select) so you should learn this technique. In doubt, just add all the parenthesis.
If you want to go through rails, I suggest content_tag (docs).
Example:
content_tag(:a, 'Comments', :name => 'comments')
<%= link_to('new button', action: 'login' , class: "text-center") %>
created an anchor tag for login.html i.g
new button
and for
new button
use
<%= link_to('new button', controller: 'admin',
action: 'login' , class: "text-center") %>