On submit this form will call the put action on the controller:
.panel-body{style: 'background:#0E0D0D;'}
=form_for #payment, method: :put, html: {id: 'edit_bank_detail'} do |f| .row.text_white
.form-group.col-lg-12
=f.label :paypal_email_address
=f.email_field 'user_paypal_email',class: 'form-control'
.row.text-center
.form-group.col-lg-12
=f.submit 'Submit', class: 'btn btn-primary text_black',data: { disable_with: "Please wait..." }
I need it to call the put or create action based on the condition:
if #payments.new_record?
# create action
else
# put action
You don't even need to specify the HTTP method in the first place. form_for and its successor form_with are smart enough to figure out both the path for the action attribute and the method by calling #persisted? on the #payment model instance that you pass as the first argument.
= form_for #payment, html: {id: 'edit_bank_detail'} do |f|
Given conventional routes this will point the form to POST /payments or PATCH /payments/:id if the record has been persisted. Rails has used PATCH instead of PUT for updates since 2012 but resources and resource still generate additional PUT routes for backwards compatibility.
I tried something like this and it works.
=f.submit 'Submit', action_name: #return.new_record? ? 'create' : 'update'
Thanks #Sebastian Palma followed your suggestion.
Related
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.
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.
So I have a posts scaffold generated in a Rails app and I've added an upvote and downvote column to the post model. I added an "upvote" button on the view file and I need to make an AJAX call and query the database when you hit the upvote button, but the upvote button has no real Rails <form> attached to it. How can I make this AJAX call and add the upvote to the database for the upvoted post?
When I make this AJAX call:
$('.up,.down').click(function(){
$.ajax({
type: 'POST',
url: '/posts',
dataType: 'JSON',
data: {
post: {
upvote: 1
}
},
success: function(){
alert('success')
}
});
});
It returns a 500 error. Where do I go form here?
You could use the :remote => true attribute on the link_to helper
for example:
<%= link_to post_upvote_path(post), :remote => true, :method => "put" %>
<%= link_to post_downvote_path(post), :remote => true, :method => "put" %>
then in config/routes.rb:
resources :posts do
put "upvote", :to => "posts#upvote", as: :upvote
put "downvote", :to => "posts#downvote", as: :downvote
end
then handle the voting in your posts controller, like you probably already are and grab the post id with params[:id] in the action
Here is an intro to rails flavored unobtrusive javascript
Update
To see the upvote and downvote routes that were created, go to the terminal and type
rake routes | grep vote
this will give you a list of all of your routes that have "vote" in the name. Or just type rake routes to get a list of all of them. The first column is the named route, just append '_path' to the end of it to use it in your app - like post_upvote_path above would be seen as
post_upvote PUT /posts/:id/upvote(.:format) posts#upvote
And in you PostsController you would want these actions:
class PostsController < ApplicationController
###
# index, show... other RESTful actions here
###
def upvote
#post = Post.find params[:id]
# code for however you are voting up the post here
end
def downvote
#post = Post.find params[:id]
# code for however you are voting down the post here
end
end
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.
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") %>