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
Related
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.
My app allows users to see a list of questions and allows them to vote for an option (uniquely). I have a users table, a questions table, an options table and an answers table.
I've created the migration to add the column message to the answers table and I can show the messages on the page, just need to actually send the message to the DB table - this is the button to allow the user to vote:
<%= link_to 'Vote', question_option_upvote_path(#question, option), data: { confirm: 'Are you sure you want to vote for this option?' }, :class => 'voteNow', method: :post %>
I'm struggling to hook up the textarea to be sent when the user clicks to vote, I'm using simply <%= text_area :message, autofocus: true %> to create my textarea on it's own. Do I need to setup a whole form for this or is there a simple way to pull the contents when the user votes? My options controller is:
class OptionsController < ApplicationController
def create
#question = Question.find(params[:question_id])
#option = #question.options.create(option_param)
redirect_to question_path(#question)
end
def upvote
#question = Question.find(params[:question_id])
#option = Option.find(params[:option_id])
Answer.create(user_id: current_user.id, question_id: #question.id, option_id: #option.id)
redirect_to question_path(#question)
end
private
def option_param
params.require(:option).permit(:option_text)
end
end
Let me know if I need to include anything else in this message!
This is an error I can not seem to figure out I believe I have it routed. This is the error
No route matches {:action=>"ticket_action", :controller=>"tickets"}
I get this error after this code
<h4>New Action</h4>
<% form_tag :action => 'ticket_action' do %>
<p><b>Description</b><br/>
<%= text_area 'description', 'description', 'rows' => 5 %><br/>
User: <%= select("actUser", "user_id", User.find(:all).collect{|u| [u.name, u.id] } )%>
<% end %>
I have this on my ticket_controller.rb is that the proper placement for that
#action
def ticket_action
#act = Action.new(
"ticket_id" => flash[:ticket_id],
"description" => params[:description]['description'],
"user_id" => params[:actUser]['user_id']
)
routes
actions GET /actions(.:format) actions#index
POST /actions(.:format) actions#create
new_action GET /actions/new(.:format) actions#new
edit_action GET /actions/:id/edit(.:format) actions#edit
action GET /actions/:id(.:format) actions#show
PUT /actions/:id(.:format) actions#update
DELETE /actions/:id(.:format) actions#destroy
tickets GET /tickets(.:format) tickets#index
POST /tickets(.:format) tickets#create
new_ticket GET /tickets/new(.:format) tickets#new
edit_ticket GET /tickets/:id/edit(.:format) tickets#edit
ticket GET /tickets/:id(.:format) tickets#show
PUT /tickets/:id(.:format) tickets#update
DELETE /tickets/:id(.:format) tickets#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
clients GET /clients(.:format) clients#index
POST /clients(.:format) clients#create
new_client GET /clients/new(.:format) clients#new
edit_client GET /clients/:id/edit(.:format) clients#edit
client GET /clients/:id(.:format) clients#show
PUT /clients/:id(.:format) clients#update
DELETE /clients/:id(.:format) clients#destroy
It would be helpful to post the route to debug this problem, your route may refer to tickets yet your class is ticket.
You should look into restful routes, especially given your use case. It seems you should really have an actions controller (ActionsController, named controllers/actions_controller.rb) and then post to the create action and provide a restful route (resources :actions)
My suggestion would be to read up on rest and rails first.
Additionally the flash isn't where you should store your ticket_id, ideally you should retrieve it in your actions controller's create action by posting to /action/ticket_action/1 and retrieving the id by accessing params[:id] in the controller. If you really must, store it in the session (session[:ticket_id] = "1") but 'rest' is where you should be headed. The flash will be removed and should only be set in the controller and then displayed on the next page, it will be deleted thereafter.
Update: ok thanks for posting your routes.
You can add the missing route like this if you want:
resources :tickets do
member do
post 'ticket_action'
end
end
But it would be better to follow this pattern:
In actions controller:
def new
#action = Action.new
end
Your form should look a bit like this, Rails will know to post to actions#create because #action is a new record (you can check #action.new_record? if you want)
<%= form_for #action do |f| %>
<%= f.text_area :description, :rows => 5 %>
<%= f.hidden_field :ticket_id, flash[:ticket_id] %>
<%= f.select :user_id, User.find(:all).collect{|u| [u.name, u.id] } %>
<%= f.submit "Create" %>
<% end %>
Then in your actions controller:
def create
#action = Action.new(params[:action])
end
or with less magic:
def create
#action = Action.new(:user_id => params[:action][:user_id],
:description => params[:action][:description],
:ticket_id => params[:action][:ticket_id])
if #action.save
redirect_to actions_path(#action, :notice => "Created action")
else
render :new # any errors will be in #action.errors
end
end
You should really be setting the ticket_id in the actions controller's new method though.
def new
#action = Action.new(:ticket_id => params[:ticket_id])
end
And then in your form:
<%= f.hidden_field :ticket_id %>
Your file name should be "tickets_controller.rb", plural.
is it possible to link to a method inside a different model? To execute on button press, or is there another way of doing this, something like an action in a controller?
Method inside staff model:
def clearleave
self.where("grade = '1'").update_all(:leave_balance => 22)
self.where("grade = '2'").update_all(:leave_balance => 25)
self.where("grade = '3'").update_all(:leave_balance => 30)
self.where("grade = '4'").update_all(:leave_balance => 35)
end
inside a view for a different model:
<%=button_to "Clear absences", {:controller => :staffs, :action => :clearleave} %>
Rails bases on the Model View Controller pattern (MVC). This means, that requests (e.g. trough the browser) are handled by your controllers. Controllers will collect the required data from the models and pass it to the views for display.
What you are probably aiming for is something like this:
Routes:
resources :staff do
member do
post :clearleave
end
end
Controller:
# StaffController
def clearleave
#staff = Staff.find(params[:id])
#staff.clearleave # this calls the method in your model
# here you could redirect to e.g. the show page for your staff
# redirect_to staff_path(#staff), :notice => "Cleared successfully"
end
Your button in the view would be:
<%= button_to "Clear absences", clearleave_staff_path(#staff) %>
<!-- not sure if a ", :method => :post" is required here as well -->
<!-- in rails 3 a link_to should also work -->
<%= link_to "Clear absences", clearleave_staff_path(#staff), :method => :post %>
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.