How can I update this vote counter dynamically with Ajax/JavaScript (Rails)? - ruby-on-rails-3

I built a simple voting system:
votes_controller.rb:
class VotesController < ApplicationController
def vote_up
#post = Post.find(params[:id])
#vote = #post.votes.create(:user_id => current_user.id, :polarity => 1)
end
end
(If there is any bad practice here, please let me know)
views/show.html.erb:
<h3><%= #post.votes.count %> votes</h3><br />
<%= link_to "Vote Up", vote_up_path(#post), :remote => true %>
(I will put this in a partial of course)
routes.rb:
get 'votes/:id/vote_up' => 'votes#vote_up', as: 'vote_up'
When I click the "Vote Up" link the vote is added to the post but I have to refresh the page in order to see the changes. How can I refresh #post.votes.count and vote.user.username dynamically with Ajax/JavaScript?

First put make a wrapper div for show.html.erb
<div class='post-<%=#post.id%>' >
<h3><%= #post.votes.count %> votes</h3><br />
<%= link_to "Vote Up", vote_up_path(#post), :remote => true %>
</div>
and in vote_up.js.erb
$("post-<%=#post.id%>").html('<%=escape_javascript #post.votes.count %>');
or something like this

Related

undefined method `model_name' in a partial rendered by different controller

I'm trying to render this form:
<form class="form-inline">
<%= simple_form_for #prospect,
:url => url_for(:action => 'create', :controller => 'prospects'),
:method => 'post' do |f| %>
<%= f.error_notification %>
<%= f.input :name, placeholder: 'Name', label: false %>
<%= f.input :email, placeholder: 'Email', label: false %>
<%= f.input :interests, placeholder: 'Tell us what you were searching for', label: false, value: params[:search] %>
<%= f.error :base %>
<%= f.button :submit, "Submit", :class=> "btn" %>
<% end %>
Using this partial:
<%= render partial: 'prospects/novideo_capture' %>
The partial is in a view controlled by Videos#index controller, and I keep getting this error: 'undefined method `model_name' for NilClass:Class'
This is my prospects controller:
class ProspectsController < ApplicationController
def index
#prospects = Prospect.all
end
def new
#prospect = Prospect.new
end
def create
#prospect = Prospect.new(params[:prospect])
if #prospect.save
render "thanks_for_interest"
else
render "novideo_capture"
end
end
I'm not sure what I'm going wrong, although I'm pretty sure it's a simple solution. I've seen a lot of similar questions around SO and tried all their answers, but none of them seem to work for this situation.
Thanks for any help...
EDIT: Adding
#prospect = Prospect.new
to the videos index controller stops the error occurring, but I don't feel it's the right way to do this. It also doesn't actually make the form use the prospects controller.
EDIT2: I now have the partial rendering correctly (I think), and my videos#index calls the partial like this:
<%= render partial: 'prospects/novideo_capture', :prospect => #prospect %>
Then simple_form in the partial looks like this:
<form class="form-inline">
<%= simple_form_for :prospect,
:url => url_for(:action => 'create', :controller => 'prospects'),
:method => 'post' do |f| %>
...
<% end %>
However it's not actually submitting the form with the prospects controller. Any ideas why?
Check your markup. You're wrapping a simple_form inside another form. Since the first form tag has no action associated with it (<form class="form-inline">), that form will submit against the current URL, which is the video#index.
You're going to want something like this:
<%= simple_form_for :prospect, :url => etc, :method => 'post', :class => "form-inline" do |f|
...
<% end %>
Losing the leading (redundant) form-inline form tag and you'll be fine.

Ruby on Rails - Multibutton combined with checkboxes and advanced search

Currently I have the situation that one button is add to the index-form. This workes perfectly in combination with the other functionalties as search and checkboxes which are also part of the index-form.
Code of the index.html.erb:
<%= form_tag order_path, :method => 'get' do %>
<p>
<%= submit_tag "Historical Search", :account_short => nil %>
<%= text_field_tag :search, params[:search] %>
</p>
<% end %>
<%= form_tag sendnext_order_path, :method => :put do %>
<%= submit_tag "Send to Desk" %><br/>
-- other code from index-form
<% end %>
Combined with the controller:
def sendnext
Order.update_all(["status_id = ? ", "2"], :id => params[:order_ids])
redirect_to order_path, notice: 'Order succesfully send to desk.'
end
Now I want to add a second button next to the Send to Desk button with another action than the excisting working one. Until now I'm not capable to realise this.
Please advice. Any feedback is welcome.
Use button_to
<%= button_to('Send to Desk', 'sendnext', :method => "put") %>
<%= button_to('Cancel Order', 'cancelorder ', :method => "put") %>
Will take care of the form for you. submit_tag is submitting the first form I believe.
For example
<%= button_to "New", :action => "new" %>
will generate
# => "<form method="post" action="/controller/new" class="button_to">
# <div><input value="New" type="submit" /></div>
# </form>"
Thanks for your help. I have found a working solution which realised my current requirements:
The index.html.erb looks like the following:
<%= form_tag updateorder_order_path, :method => :put do %>
<%= submit_tag "To Desk" %><br/>
<%= submit_tag "Cancel Order" %>
-- other code like data fields
<%end %>
The controller.rb looks like the following:
def updateorder
if params[:commit] == "To Desk"
Order.update_all(["status_id = ? ", "2"], :id => params[:order_ids])
redirect_to order_path, notice: 'Order(s) successfully send to desk.'
elsif params[:commit] == "Cancel Order"
Order.update_all(["status_id = ? ", "3"], :id => params[:order_ids])
redirect_to order_path, notice: 'Order(s) successfully cancelled.'
else
Order.update_all(["status_id = ? ", "5"], :id => params[:order_ids])
redirect_to order_path, notice: 'Order(s) successfully updated.'
end
end
The routes.rb contains the next code:
resources :orders do
put 'updateorder', :on => :collection
end

Why am I getting "no route matches [POST]" in my nested resources?

I have a project that contains projects that have todos that have tasks. When I try to create a new task, I get this error when I submit:
No route matches [POST] "/projects/1/todos/19/tasks/new"
Here is my form:
<%= form_for [#todo, #todo.tasks.new], :url => new_project_todo_task_path(#project, #todo) do |f| %>
<div class="field">
<%= f.label :description, "Description" %><br />
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %> or <%= link_to "Cancel", "#", :id => "cancel_new_task_link" %>
</div>
<% end %>
Here is my controller:
class TasksController < ApplicationController
before_filter :authenticated?
before_filter :get_project_and_todo
respond_to :html, :xml, :json, :js
def new
#task = #todo.tasks.new
end
def create
#task = #todo.tasks.new(params[:task])
if #task.save
respond_with #todo, :location => project_todo_path(#project, #todo)
else
render "new"
end
end
private
def get_project_and_todo
#project = Project.find(params[:project_id])
#todo = #project.todos.find(params[:todo_id])
end
end
Here are my routes:
resources :projects do
resources :todos do
resources :tasks
end
end
Thanks
Your URL should not be new_project_todo_task_path(#project, #todo). You don't need to specify the URL here as Rails will imply it from the parameters passed in to form_for.
If the final object is a new object and not persisted in the database then it will make a POST request to, in this case, /projects/:project_id/todos. You're declaring in your example that you want to make a POST request to /projects/:project_id/todos/new, for which there is no POST route and that is why it's failing.

2 render templates (from devise) on one page (rails 3)

I've installed devise for my rails app, i can go to the sign in page or the sign up page. But I want them both on a welcome page...
So I've made a welcome_page_controller.rb with the following function:
class WelcomePageController < ApplicationController
def index
render :template => '/devise/sessions/new'
render :template => '/devise/registration/new'
end
end
But when i go to the welcome page i get this error:
NameError in Welcome_page#index
Showing /Users/tboeree/Dropbox/rails_projects/rebasev4/app/views/devise/sessions/new.html.erb where line #5 raised:
undefined local variable or method `resource' for #<#<Class:0x104931c>:0x102749c>
Extracted source (around line #5):
2: <% #header_title = "Login" %>
3:
4:
5: <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
6: <p><%= f.label :email %><br />
7: <%= f.email_field :email %></p>
8:
Does anybody knows a solution for this problem? Thanks in advance!
Does it have to do with the fact that it is missing the resource function? in the welcome_page controller? It's probably somewhere in the devise controller...?
Regards,
Thijs
Here's how I managed to did it.
I've put a sign up form in my home#index
My files:
view/home/index.html.erb
<%= render :file => 'registrations/new' %>
helper/home_helper.rb
module HomeHelper
def resource_name
:user
end
def resource
#resource = session[:subscription] || User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
def devise_error_messages!
return "" if resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count,
:resource => resource_name)
html = <<-HTML
<div id="error_explanation">
<h2>#{sentence}</h2>
<ul>#{messages}</ul>
</div>
HTML
html.html_safe
end
end
You need that part because Devise works with something called resource and it should be defined so you can call your registration#new anywhere.
Like that, you should be able to register. However, I needed to display errors on the same page. Here's what I added:
layout/home.html.erb (the layout used by index view)
<% flash.each do |name, msg| %>
# New code (allow for flash elements to be arrays)
<% if msg.class == Array %>
<% msg.each do |message| %>
<%= content_tag :div, message, :id => "flash_#{name}" %>
<% end %>
<% else %>
# old code
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<% end %> #don't forget the extra end
<% end %>
I found this code here
And here's something I created: I saved my resource object if invalid in a session so that the user hasn't to fill every field again. I guess a better solution exists but it works and it's enough for me ;)
controller/registration_controller.rb
def create
build_resource
if resource.save
if resource.active_for_authentication?
# We delete the session created by an incomplete subscription if it exists.
if !session[:subscription].nil?
session[:subscription] = nil
end
set_flash_message :notice, :signed_up if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => resource.inactive_message.to_s if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords(resource)
# Solution for displaying Devise errors on the homepage found on:
# https://stackoverflow.com/questions/4101641/rails-devise-handling-devise-error-messages
flash[:notice] = flash[:notice].to_a.concat resource.errors.full_messages
# We store the invalid object in session so the user hasn't to fill every fields again.
# The session is deleted if the subscription becomes valid.
session[:subscription] = resource
redirect_to root_path #Set the path you want here
end
end
I think I didn't forget any code. Feel free to use whatever you need.
Also, you can add your sign in form in the same page (something like that:)
<%= form_for("user", :url => user_session_path) do |f| %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.submit 'Sign in' %>
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
<%= link_to "Forgot your password?", new_password_path('user') %>
<% end %>
Cheers !

Voting app in rails 3: how do I link to vote method?

To teach myself Rails, im building an extremely simple Voting app.
There are 2 models, Question and Option. Question has_many Options and Option belongs_to Question.
Using the standard scaffolding, I have reached a stage where you can add a question, view it, and add options to it and see these options.
What I would like to do now is add the code that increases an option.count value by one when clicking on a link. I have a vote_up method in the Option model:
class Option < ActiveRecord::Base
validates :text, :presence => :true
belongs_to :question
def vote_up
self.count += 1
end
end
My Options controller looks like:
class OptionsController < ApplicationController
def create
#question = Question.find(params[:question_id])
#option = #question.options.create(params[:option])
redirect_to question_path(#question)
end
end
My Question model looks like:
class Question < ActiveRecord::Base
validates :text, :presence => {:message => 'A question normally has text...'}
has_many :options, :dependent => :destroy
def vote
# Maybe the vote code will go here???
end
end
And my Question controller has the usual new, create, edit, destroy methods that the scaffold creates. V little customisation here.
My show.html.erb view where I would like to put the link to the vote method looks like:
<p id="notice"><%= notice %></p>
<p>
<b>Question <%= #question.guid %></b>:
<%= #question.text %>
</p>
<% if #question.options.count == 0 %>
<p>Shame! there are currently no options to vote on. Add some! </p>
<% elsif #question.options.count == 1 %>
<p>One option in a vote is a dictatorship... Maybe add some more?</p>
<% end %>
<h2>Options:</h2>
<% #question.options.each do |option| %>
<p>
<%= option.text %>: ** Link to vote here!
</p>
<% end %>
<h2>Add an option to vote on</h2>
<%= form_for([#question, #question.options.build]) do |f| %>
<div class="field">
<%= f.label :text %><br />
<%= f.text_field :text %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% if #question.options.count == 0 # Only show edit if no options saved. %>
<%= link_to 'Edit', edit_question_path(#question) %> |
<% end %>
<%= link_to 'Back', questions_path %>
So what I am trying to do is add a "vote" link next to each option that calls the vote_up method in the options model. This is probably laughably easy, but i've hit a wall and would really appreciate any help.
Also, any suggestions on how to do this better would be welcome!
Thanks
Simon
I think #oded-harth has showed a right way, but I have two remarks:
First of all, Rails is a beautiful language and is written to simplify our developer lives ;) When programming in Rails you must never forget that. With this in mind, I want to point you to "increment()" method. So you can simply up-vote without unnecessary += 1. To down-vote use decrement(). I believe you can use it like this: option.increment(:count)
Second, I think it's a little dirty to have a whole form for a simple vote action. You can use something like this
<%= link_to "Vote Up", :url => { :action => :vote_up, :option_id => option.id }, :method => :put %>
To make it work you'll have to set your route something like this:
resources :votes
put :vote_up
end
What I would do is make the vote_up method in the controller:
def vote_up
option = Option.find(params[:option_id])
option.count += 1
redirect (to where do you want...)
end
And in the view I would call that method this way:
<%= form_for( option, :url => { :action => "vote_up", :option_id => option.id} ) do |f| %>
<%= f.submit("vote up") %>
<% end %>