I am implementing an up-voting system on an app I am building very similar to the one here on stackoverflow. When a user clicks upvote or downvote, an ajax request is sent to one of my controllers which them updates a couple of tables. Once that is done, I use the respond_to to routes to a js.erb file that executes jquery to update the user display. However, the jquery is not being executed. When I lick upvote/downvote, the controller code executes properly (I can see it in the rails console), but the user display is not updated. Upon refresh the user display is updated, however, not asynchronously. I know the jquery is not being executed b/c in the fire bug console I can see my jquery code - it just is not being applied. Here is my upvote controller code:
def upvote
#post = Post.find(params[:id])
#user_vote = current_user.votes.where(post_id: #post.id).first
if #user_vote.blank?
Vote.create(user_id: current_user.id, post_id: #post.id, vote: 1)
#post.upvotes += 1
elsif #user_vote.vote.to_i == 0
#user_vote.vote = 1
#post.upvotes += 1
elsif #user_vote.vote.to_i == 1
return redirect_to :back, :alert => 'You have already upvoted the post'
elsif #user_vote.vote.to_i == 2
#user_vote.vote = 1
#post.downvotes -= 1
#post.upvotes += 1
end
respond_to do |format|
if #post.save and #user_vote.save
format.js { }
else
format.html { redirect_to :back, :alert => 'There was an error in upvoting the post' }
end
end
end
This is my upvote.js.erb file:
$('#post-action-<%= #post.id %>').html("upvote");
// this normally renders a partial (below), but I am rendering "upvote" until I can fix this
// escape_javascript(<%= render :partial => 'post_voting', :locals => { post: #post, user_vote: #user_vote } %>)
Here is my html:
<div class="post_actions" id='post-action-<%= "#{post.id}" %>' >
<%= render :partial => 'post_voting', :locals => { post: post, user_vote: current_user.votes.where( post_id: post.id ).first } %>
</div>
Here is my firebug console output: http://i.imgur.com/H6zXJ.png
EDIT
I noticed I am getting an error in on my server:
Started GET "/assets/bootstrap.js?body=1" for 127.0.0.1 at 2012-08-09 06:33:42 -0700
Served asset /bootstrap.js - 304 Not Modified (0ms)
Your code seems OK and should execute. You might do an additional check to update a fixed id instead of the calculated id.
$('#updateme').html("upvote");
and add this id in your html somewhere. If this works, you are certain the ajax-call executes completely, and the problem is just the id you are referencing.
Have you checked your html-source that there is an id "post-action-2"?
After messing around with the jquery, I noticed some things. Firstly, I needed to minify my code (taking out all comments and unused spaces). I took this:
$('#post-action-<%= #post.id %>').html("upvote");
// this normally renders a partial (below), but I am rendering "upvote" until I can fix this
// escape_javascript(<%= render :partial => 'post_voting', :locals => { post: #post, user_vote: #user_vote } %>)
and changed it to this:
$('#post-action-<%= #post.id %>').html("upvote");
The code was then working and the user display was updating to show "upvote." Perfect. So then I replaced the above code with this:
$('#post-action-<%= "#{#post.id}" %>').html("<%= escape_javascript(render :partial => 'post_voting', :locals => { :post => #post, :user_vote => #user_vote }) %>");
and KAPOW! It works. So happy to have solved this! MINIFY YOUR JQUERY PEOPLE!
Related
So I am trying to do an AJAX request in my form_tag. Basically, a user can enter a song name or an artist's name and I want to simply post what they searched for into a playlist (very simply here)
These are my files:
My form
<%= form_tag({:action => 'capp'}, method:'GET', class: 'ui form', remote: true , id: 'song_form') do %>
<div class="one field">
<div class="field" style="width: 100%">
<%= text_field_tag :query %>
</div>
</div>
<%= submit_tag("Search Song" , class: 'ui large green button') %>
<% end %>
Where I am trying to put the query entered
<div id="query"></div>
My 'app' Controller which has that action
class AppController < ApplicationController
respond_to :html, :js
def create
passcode = params[:passcode]
if passcode != nil && passcode.length > 1
redirect_to :controller => app, :action => capp, :passcode => passcode
end
end
def capp
#passcode = params[:passcode]
#query = params[:query] <-- I used pry and the query was being updated but the view itself was not.
if #query != nil
respond_to do |format|
format.html
format.js {}
end
end
end
end
My capp.js.erb file
addSongToPlaylist("<%= #query %>");
// For the record. I tried to also do this:
// addSongToPlaylist("<%= j render #query %>"); <-- But the app errored out saying render was an undefined method name...and all the online tutorials have their function rendering for some reason.
My capp.js file
function addSongToPlaylist(query) {
var c = "</br>" + query;
$("#query").append(c);
}
All I am hoping for is that the query I entered in the form above be placed in the
location below. But the as soon as I hit submit, the application doesn't do anything. the request goes through (I can see the server sending the GET request) but the view doesnt update. Please help!
I followed the tutorial on doing AJAX calls and for some reason it's still not working
Firstly, why aren't you using a path helper for your form_tag?
You'd benefit from using the likes of:
#config/routes.rb
get "/search/:query", to: "application#search", as: :search
This will allow you to use the following:
<%= form_tag search_path, remote: true, method: :get %>
<%= text_field_tag :query %>
<%= submit_tag "Search" %>
<% end %>
This will send a get request to your /search/:query path, which you'll then be able to process with your JS (explained in a second):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
respond_to :html, :js
def search
#passcode = params[:passcode]
#query = params[:query]
end
end
This should handle the request for you, looking to open app/views/application/search.js.erb
--
JS
For sake of clarity, let's just put the function into your called JS file:
#app/views/application/search.js.erb
$("#query").append("<br> <%=j #query %>");
This will do what your addSongToPlaylist function is doing.
Considering you've populated the #query variable, and are passing the ajax properly, this should work.
Debug
One of the main problems you'll have is that you're not debugging your application.
In this instance, there are a number of potential issues which could prevent your Ajax from firing:
Your ajax isn't being passed (IE JQuery is not loaded)
Your path is incorrect
Your controller isn't handling the request properly
You're not handling the JS correctly
The way to initially test is to look at the network tab of your browser's developer console:
To access it, right-click, inspect element, then select network. This will give you a list of all the requests you've sent to your server, and their responses.
When you click your "form submit" button - check to see whether a new request is invoked. If not, it will generally mean you don't have jquery in your app (which is required by the Rails UJS).
--
Next, you need to check your Rails console to see if you're getting any requests. If not, it will not show anything coming through.
What you're expecting is to see a new set of commands triggered when you submit your "search" form. This will show you which controller#action has been triggered. If it's right, move forward.
--
Finally, you need to check your JS.
You can do this by using alerts:
#app/views/application/search.js.erb
alert("<%=j #query %>");
$("#query").append("<br> <%=j #query %>");
If the alert fires positively, you've got a hit.
I have my onclick event working but when it comes to displaying the partial it:
only displays the text:
<%= escape_javascript(render(:partial => payment)) %>
I want it to fetch the parial and display it
here is my code:
$(".payment").append("<%= escape_javascript(render(:partial => payment)) %>");
tried with "" and '' around payment
full jquery code for this
$(document).ready ->
$('.plans').change ->
$(".payment").append("<%= escape_javascript(render(:partial => 'payment')) %>");
edit:
this worked for me if anyone looks at this in the future since i use devise:
devise_scope :user do
get 'registrations/toggle_partial' => "registrations#toggle_partial"
end
put the toggle_partial.js file in
views/devise/registrations
everything else that NickM put there is great....
also remember the '' around the partial name
Based on the 'full jquery' code you have above I'm guessing you have this in a coffeescript file, which does not have access to the render method.
You're going to have to set up a route that hits a controller action that renders the JS, like this:
routes.rb:
get 'controller_name/toggle_partial' => "controller_name#toggle_partial"
In that controller:
def toggle_partial
respond_to do |format|
format.js
end
And in views/controller_name add a file called toggle_partial.js with these contents:
$(".payment").append("<%= escape_javascript(render(:partial => payment)) %>");
Then in your coffeescript file do something like this:
$('.plans').change ->
$.ajax(
type: 'GET'
url: '/controller_name/toggle_partial'
success: ( data, status, xhr ) ->
)
Sorry if the indentation is off on the coffeescript example. The bottom line is that you don't have access to render in the coffeescript file so you have to make a work-around.
Hope this helps.
Long story short, I have a button. On clicking it, I want an ajax request to be triggered which gets flash[:notice] and displays it in a div in$
Here is my shortened view:
<input type="button" id="search" value="display"/>
<div id="notice">
</div>
My ajax request in the view:
$("#search").submit(function(){
$.ajax({
type: "POST",
url: //url to my show action
success: function(data){
/*$("#notice").html("<%= flash[:notice] %>");
$("#content").html(data);*/
}
});
return false;
});
My controller:
def HomeController < ActionController::Base
def index
end
def show
respond_to do |format|
format.js { flash[:notice] = "" + count.to_s + " results found for " + params[:query][:search_key] + "" }
end
#render :partial => 'search'
end
end
My show.js.erb
#app/views/dashboard_home/show.js.erb
$("#notice").html("<%=j flash[:notice] %>");
$("#content").html("<%=j render partial: "search" %>");
The problem is when I click on button, the notice is displayed fine. But the same notice persists on the next clicks too. The search partial contains the table Please help!
Here is an example that I got working, thanks to Rich Peck's answer. I needed to use flash.now to make sure the flash notice didn't persist.
AJAX trigger in the view:
<%= link_to "Email report", users_path, remote: true %>
Controller:
# app/controllers/users_controller
class UsersController < ApplicationController
def index
# do some things here
respond_to do |format|
format.js { flash.now[:notice] = "Here is my flash notice" }
end
end
end
Rendered view:
# app/views/users/index.js.erb
$("#flash").html('<%= j render partial: "shared/notice_banner" %>');
where the flash notice is displayed in the layout:
# app/views/layouts/application.html.erb
<div id="flash">
<% if notice.present? %>
<%= render partial: "shared/notice_banner" %>
<% end %>
</div>
# app/views/shared/_notice_banner.html.erb
<div data-alert class="alert-box">
<%= notice %>
×
</div>
Sessions
the same notice persists on the next clicks too
This is caused by the flash being stored in the session variable of Rails:
The flash is a special part of the session which is cleared with each
request. This means that values stored there will only be available in
the next request, which is useful for passing error messages etc.
The problem you have is that since I don't think ajax counts as a new request (need reference for this), the data will persist into the next time you request via HTTP.
--
Fix
I would initially try this:
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
The main problem you have is you're processing the flash variable in your JS using the ERB preprocessor. This is an issue as it means you won't be able to use asset precompile to help it work.
After looking at this question, why not try using the after_filter callback, like this:
#app/controllers/home_controller.rb
Class Home < ActionController::Base
after_filter { flash.discard if request.xhr? }, only: :show
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
end
--
Update
You should include the success functionality in your show.js.erb:
#app/views/home/show.js.erb
$("#notice").html("<%= flash[:notice] %>");
This means you can remove the whole ajax call from the application.js, and replace with the remote: true for your search form:
#app/views/search/index.html.erb
<%= form_tag home_show_path, remote: true %>
The reason this works is because when you use the format.js respond block, Rails will load the [action].js.erb file in your views. Considering this only happens after the action has been completed, it's equivalent to the success function of your ajax.
By doing this, you'll be able to remove the entire ajax function from your application.js, and replace with the UJS version, as described above
My plan was to update a partial via a klick-event where I fetch some data via ajax.
So i opened my assets/javascripts directory and put the code in the js file of the module I planed to call it on. I managed to fetch the data and to call some "html() or text()" on the div. But as soon as I tried to call
.html("<%= escape_javascript( render( :partial => 'job') ) %>")
my controller told me unknown method "render". So I red in many other Threats that the assets directory was not build for static content.
But now comes the question where to put it if not in assets?
I tried to put it in the view/model folder (with the same name as the aktion 'show'). But it wouldn't load. Yes I told the Controller in the show action to respond_to format.js.
So where should I put the js-File and how to call it? Or is there even a method to let it in the assets-directory and beeing able to call render?
Thanks for your answer! Sadly I do exactly that but I doesnt work. So here is my Code:
/app/views/events/show.js.erb.coffee:
$(document).ready ->
url = window.location
url = String(url).split('/')
event_id = url[$.inArray( "events" , url) + 1]
$('.position').click ->
position_id = $(this).attr("id")
$.ajax '/events/'+event_id+'/eventpositions/'+position_id+'/eventjobs'
dataType: 'json'
success: (result)->
$( '#'+position_id ).find('.jobs').html("<%= escape_javascript( render( :partial => 'job') ) %>")
alert result[1].job_id
/app/views/events/show.html.haml:
%p#notice{:xmlns => "http://www.w3.org/1999/html"}= notice
%p
%b Name:
= #event.name
\
%b Plz:
= #event.plz
%p
%b Personal:
- #eventpositions.each do |p|
.position{:id => "#{p.id}"}
%b
Position: #{Position.find(p.position_id).name} Anzahl: #{p.quantity} Aufträge: #{p.eventjobs.all.count}
.jobs
= link_to 'Neuer Auftrag', new_event_eventposition_eventjob_path(#event.id,p.id)
%br
%br
= link_to 'Neue Position', new_event_eventposition_path(#event.id)
= link_to 'Edit', edit_event_path(#event)
|
\#{link_to 'Back', events_path}
/app/views/events/_job.html.haml:
.jobs
- eventjobs.each do |j|
User: #{User.find(Job.find(j.job_id).user_id).email}
%br
/app/controllers/events_controller.rb:
def show
#agency = Agency.find(current_agent.agency_id)
#event = #agency.events.find(params[:id])
#eventpositions = #event.eventpositions.all
respond_to do |format|
format.html # show.html.erb
format.json { render json: #event }
format.js
end
end
So the Code does work without Javascript. All renders and is fine. But my main Problem now is that it wont even react to a click event if I place it in the app/views/events Folder. As soon as I place it in the assets directory I works like a charm, but wont render.
So what am I missing? Why does my js Code does not get loaded?
Thanks a lot for your help!
Generally for this kind of thing, I'll name it the same as a particular action, such as show.js.erb, and then use the format.js as you have in the controller. If you're using coffeescript, it will need to be named show.js.coffee.erb. If this does not work, can you post the code of your view where this onclick event is setup?
Edit for using the show javascript code
Because you're using the show action, should just be able to do this:
= link_to "Show", #event, :remote => true, :format => :js
Adjust the event variable there as you need to, but it should work
I know this isn't valid code, but is there a way to do something like this in Rails?:
render "$('#dialog').replaceWith(#{render :action => 'new.html.erb'});"
What I'm trying to do basically is replace the contents of a JS dialog with what is/would be returned from calling render 'new.html.erb'.
Edit for #Devin M:
controller action:
def new
#act = Act.new(:user_id => current_user.id)
end
def create
#act = Act.new(params[:act])
if #act.valid?
#act.save
else
render :action => :new
end
end
new.js.erb:
$('#dialog').replaceWith("<%= escape_javascript(render(:action => 'new.html.erb')) %>");
Full error:
Showing app/views/acts/new.js.erb where line #1 raised: undefined method `formats' for nil:NilClass
You should split this code out into a seperate view since including it in the controller would go against the ideas of MVC. I would update the controller to respond to JS requests using some code like this in the action I wanted to modify:
respond_to do |format|
format.html { redirect_to #item }
format.js
end
And create a view like this with the extenstion .js.erb:
$('#dialog').replaceWith("<%= escape_javascript(render :partial => "new.html.erb", :locals => { :act => #act }) %>");
You can then trigger this JS with a remote link to the action or by adding your own UJS.