rails with ajax, respond_to confusion - ruby-on-rails

i have a voting system which i would like the voting response to be asynchronous.
in my 'show' view i have...
<%= render 'score' %>
which renders
<% if #user.voted?(#lesson) %>
<%= render 'voted' %>
<% else %>
<%= render 'unvoted' %>
<% end %>
and in turn renders my _voted.html.erb
#will display different images, colored or not based on selection
<% if #user.up_voted?(#lesson) %>
<div class = "up_arrow">
<%= link_to image_tag("up_color.png", alt: "Uncheck vote"),
clear_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_uncolor.png", alt: "Vote down"),
down_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<!-- user down voted then -->
<% else %>
<div class = "up_arrow">
<%= link_to image_tag("up_uncolor.png", alt: "Vote up"),
up_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_color.png", alt: "Uncheck vote"),
clear_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<% end %>
and my unvoted.html.erb is
<div class = "up_arrow">
<%= link_to image_tag("up_uncolor.png", alt: "Vote up"),
up_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
<div class="average_score"><%= #lesson.votes %></div>
<div class= "down_arrow">
<%= link_to image_tag("down_uncolor.png", alt: "Vote down"),
down_vote_lesson_url(#lesson), :method => :post, remote: true %>
</div>
and then lastly in my controller i have..
def up_vote
lesson = Lesson.find(params[:id])
current_user.up_vote!(lesson)
respond_to do |format|
format.html { render :action => 'show'}
format.js { ???????? }
end
end
i have a few more actions such as down_vote but its very similar.
i guess im not really sure what to put inside my format.js block. ive tried puttings things like redirect_to lesson_path(lesson) and also render 'score' but the voting images don't get updated.
could someone please help? thanks!
UPDATE # dhoelzgen
i changed it to...
def up_vote
lesson = Lesson.find(params[:id])
current_user.up_vote!(lesson)
render :score, :layout => false
#redirect_to lesson_path(lesson)
end
i put the render :score, :layout => false in my other actions as well.
and in my view i have
<div id="surrounding_container">
<%= render 'score' %>
</div>
along with
$('.up_arrow')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
$('.down_arrow')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
in my lessons.js.coffee.
am i missing something? the images still don't change asynchronously

You have several options, I would suggest just returning the rendered score (not as js / json) and then putting it to (surrounding) div tag.
Render the score without the layout like this
render :score, :layout => false
and then use JavaScript (CoffeeScript in my example) to replace the content of the div tag
$('.up_arrow a')
.bind 'ajax:success', (event, data) ->
$('#surrounding_container').html($(data))
Alternatively, you can return the result as json and modify the page by hand, but I think the option above results in less effort.

Related

using acts_as votabable gem for likes and want to change the heart color to red permanently after user has liked

I am using the acts_as votable gem and I manage to change the heart color to red after a user has clicked on the heart by using javascript. However, once the server refreshes the heart goes gray again and there is no indication whether a user has liked the current post or not. How can I change the color permanently after a user has liked. My idea is to set a boolean to true in the vote table and then check that somehow in the view. But I am not sure how to implement it exactly. Any help would be much appreciated.
lists_controller.rb
def vote
if !current_user.liked? #list
#list.liked_by current_user
elsif current_user.liked? #list
#list.unliked_by current_user
end
vote.js.erb
<% if current_user.liked? #list %>
$('.fa-heart').addClass('liked');
<% else %>
$('.fa-heart').removeClass('liked');
<% end %>
$('.likes-count').html("<%= #list.get_upvotes.size %>")
show.html.erb
<div class="like">
<%= link_to like_list_path(#list), :class => "like-btn", method: :put, remote: :true do %>
<i class="fa fa-heart"></i>
<% end %>
<%= link_to "", "data-toggle" => "modal", "data-target" => "#likeModalLong" do %>
<span class="likes-count"><%= #list.get_upvotes.size %></span>
<% end %>
</div>
You can create a helper to make the liked css class persistent in you view:
application_helper
def liked_list(list)
liked = current_user.liked?(list) ? 'liked' : ''
content_tag(:i, '', class: "fa fa-heart #{liked}")
end
show.html.erb
<%= link_to like_list_path(#list), :class => "like-btn", method: :put, remote: :true do %>
<%= liked_list(#list) %>
<% end %>

Using ajax to render partial

In my rails app I have a model Song. On one of the user's profile pages user_music_path(#user) I'm rendering all of their songs.
People can also like these songs, which is set up and working, I just want to use AJAX instead of page refreshes. The problem I'm having is when using ajax, I get the error undefined local variable or method "song" when I'm clicking the link "like".
Here is some of my code:
User's Music Page views/users/music.html.erb
...
<ul class="playlist list-group">
<%= render #songs %>
</ul>
views/songs/_song.html.erb
<li class="list-group-item">
<p class="pull-right">
<%= render :partial => 'songs/like_button', :locals => {:song => song} %>
<%= render :partial => 'songs/likes', :locals => {:song => song} %>
</p>
</li>
views/songs/_like_button.html.erb
<% if current_user.voted_on?(song) %>
<%= link_to "Unlike", unlike_song_path(song), :method => :post, :remote => true %>
<% else %>
<%= link_to "Like", like_song_path(song), :method => :post, :remote => true %>
<% end %>
views/songs/_likes.html.erb
<%= song.votes.count %>
views/songs/like.js.coffee
$("p.pull-right").html('<%= render :partial => "songs/like_button", :locals => {:song => song} %><%= render :partial => "songs/likes", :locals => {:song => song} %>');
controllers/songs_controller.rb
def like
begin
#vote = current_user.vote_for(#song = Song.find(params[:id]))
#vote.save
respond_with #song.user, :location => user_music_path(#song.user)
rescue ActiveRecord::RecordInvalid
redirect_to #song
end
end
And as mentioned earlier, the :like action worked perfectly before I starting using AJAX. I've also added respond_to :html, :js to my songs_controller. And the error I get when I try to like the song is ActionView::Template::Error (undefined local variable or method "song" for #<#<Class:0x000001028de9d0>:0x00000106ecd9f0>):
Where you have
<%= song.name %>
try instead
<%= #song.name %>
And do the same with your link_to paths and your render calls.
Do rake routes and use of of them for the link, e.g. (pseudo-code) link_to 'song', song_audio_path

Is it possible to use redirect_to when it's called by remote ajax?

Now this action in this controller is called by remote ajax call.
Only when the checkbox[:call] was checked and it's submit,
I'd like make it to redirect to ......_path with flash message.
Is it possible? if possible, how?
controller
.....
if params[:comment][:call] == "1"
flash[:notice] = "you checked and submit!"
redirect_to ....._path
else
ajax page reload
end
view Form (remote with ajax)
<%=form_for(([#community, #comment]), :remote => true, :class => 'form' ) do |f| %>
<%= f.check_box :call, :label => 'call' %> call
<div class="form-actions">
<div class="input-append">
<%= f.text_field :body, :id => "input", :class => "box" %>
<button type="submit" class="btn">submit</button>
</div>
</div>
<% end %>
Comment model
attr_accessible :icon, :call
....
def call
#call ||= "0"
end
def call=(value)
#call = value
end
You will have to write this code in js instead of controller.
In controller:
def action_name
....
respond_to do |format|
format.js
end
end
Make a action_name.js.erb file and write this code:
<% if params[:comment][:call] == "1" %>
<% flash[:notice] = "you checked and submit!" %>
window.location.href = <%= some_url %>
<% else %>
$("#some_div_id").html("something") //ajax page reload
<% end %>
Hope this will work for you.
No, it is not possible.
You have to render javascript that will change the window location:
window.location.href = 'http://your new location';
Of course, in your js view, you are able to use dynamic value for the url using:
window.location.href = '<%= #a_controller_variable %>';

How do I implement a show all comments feature in ruby on rails?

I'm implementing show/hide feature for users comments.
Discussed here: https://stackoverflow.com/a/10174194/439688
My aim was to:
1. Limit the default shown comments to 2.
2. Have a span with text that states the number of total comments for that particular micropost and when clicked by a user have it expand and show all comments for that micropost. I would be using Jquery/Ajax to hide, show, prepend etc.
The first change was to limit the amount of comments shown to the user and I achieved this by creating a method in my helper called "comments" and here I pass in the id of the micropost the comment belongs to.
def get_comments(micropost_id)
Comment.limit(2).order("created_at DESC").where(:micropost_id => micropost_id)
end
Now the each loop that loops through each comment will only show the 2 most recent comments.
<<% #microposts.each do |m| %>
<% if m.poster_id.nil? %>
<div class="postHolder">
<nav class="micropostOptions">
<ul class="postMenu">
<li class="deletePost"><%= link_to content_tag(:span, "Delete post"), m, :method => :delete, :confirm => "Are you sure?", :title => m.content, :class => "message_delete", :remote => true %>
</li>
<li class="disableCommenting"><%= link_to content_tag(:span, "Pause commenting"), "2" %></li>
<li class="blockCommenter"><%= link_to content_tag(:span, "Block commenter"), "3" %></li>
<li class="openInNewWindow"><%= link_to content_tag(:span, "Open in new window"), "4" %></li>
<li class="reportAbuse"><%= link_to content_tag(:span, "Report abuse"), "5" %></li>
</ul>
</nav>
<%= link_to image_tag(default_photo_for_current_user, :class => "poster_photo"), current_users_username %>
<div class="post_content">
<div class="post_container">
<div class="mainUserNameFontStyle"><%= link_to current_users_username.capitalize, current_users_username %> - <div class="post_time"> <%= time_ago_in_words(m.created_at) %> ago.</div>
</div>
<%= simple_format h(m.content) %> </div>
<div class="commentsCount">
<%= content_tag :span, pluralize(m.comments.count, 'comment'), :class => "view_all_comments" if m.comments.any? %>
</div>
<% if m.comments.any? %>
<% comments(m.id).each do |comment| %>
<div class="comment_container">
<%= link_to image_tag(default_photo_for_commenter(comment), :class => "commenter_photo"), commenter(comment.user_id).username %>
<div class="commenter_content"> <div class="userNameFontStyle"><%= link_to commenter(comment.user_id).username.capitalize, commenter(comment.user_id).username %> - <%= simple_format h(comment.content) %> </div>
</div><div class="comment_post_time"> <%= time_ago_in_words(comment.created_at) %> ago. </div>
</div>
<% end %>
<% end %>
<% if logged_in? %>
<%= form_for #comment, :remote => true do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :micropost_id, :value => m.id %>
<%= f.text_area :content, :placeholder => 'Post a comment...', :class => "comment_box", :rows => 0, :columns => 0 %>
<div class="commentButtons">
<%= f.submit 'Post it', :class => "commentButton", :disable_with => "Post it" %>
<div class="cancelButton"> Cancel </div>
</div>
<% end %>
<% end %>
</div>
</div>
From here this is where it gets confusing for me. I got slightly further using link_to but then decided I'd prefer not to have the url to the comments count show in the browser status bar. This is why I switched to using span.. but now it's not quite easy to do what I wish to do as I can't use the link_to/remote => true now.
How do I make it so when a user clicks the comment count span an ajax call is made pointing to:
def load_comments
#load_comments = Comment.where(:micropost_id => params[:id])
respond_to do |format|
format.js { render :load_comments }
end
end
I thought about putting a click function in users.js but how would I pass the params of the micropost that is in the each loop in the code above into users.js? I don't think it's possible.
All my comment posting is done via ajax but because I used forms for these it was so much easier for me to just add remote => true and create some js templates and do something on success of ajax post.
Not sure if I'm even going about this the right way. I'd appreciate some help/advice from more experienced rails programmers.
Kind regards
Rails partial
#Display all the comments based on local passed to this partial
# Initially pass limit as 2(or whatever you want). then on click of span pass limit as nil. then you can check if limit is nil you can query the model without limit specifier.
<% #comments = Comment.custom_find(#your_params) %>
<% #comments.each do |comment| %>
<%= comment.title %>
<% end %>
javascript/jquery
function load_all_comments(id)
{
new Ajax.Updater('show_comments',
'<%=url_for(:controller => "your_controller", :action => "your_action")%>', {
parameters: {'id':id },
method: 'get',
onSuccess: function(request){
div_comments = document.getElementById("partial_comments_list");
div_comments.innerHTML = request.responseText;
}
});
} // you can call this js function on span click. use jquery if you want.
Controller:
Then inside your_action of your_controller, dont forget to render the partial
render :partial => "show_comments", :layout => false
Edit:
you can even pass locals to your partial
render :partial => "show_comments", :locals => {:post => #post}
Using this every time your partial view will get updated, on the basis of locals you pass.
of course this is just an example not a complete code/solution.
There may be better ways. but this worked fine for me.
Another option is to just output all of the comments and hide the ones you don't want to show first. <div class="hidden_comments" style="display:none;"> a comment </div>
Then just have some javascript to show them when the span is clicked?
<script type="text/javascript">
$("#span_id").click(function() {
$('.hidden_comments').show();
});
</script>
This works great if you do not don't have a ton of comments.
If you really want to do it your way, I have done it before but it gets messy.
Put this in your application.js
$('.comment_span').live('click', function () {
$.get(this.data_url, null, update_row, 'json');
return false;
});
Your span would look like this:
<span class="comment_span" data_url="http://website.com/resource/more_comments">
show all comments
</span>
This example returns the data as json, so I used the update_row function to update replace the comments data.
function update_row(data, status) {
$("#comments-table").append(data.html);
};
Here is what my controller looked like:
def more_comments
#comments = Comments.all
if #comments
respond_to do |format|
format.js {
render :json => {
:html => render_to_string(:partial => "comments"),
}.to_json
}
end
end
end
You should do this via your index action.
Pass a param to it to determine if you want to show all comments or just the current set (I'd use will_paginate to handle this.
Haven't looked too deep into your code as I'm on my phone right now, but something like this:
class CommentsController < ApplicationController
def index
If params[:show_all] == "true"
#comments = Comment.all
else
#comments = Comment.where(foo: bar).paginate(per_page: 2, page: params[:page])
end
end
Then you have it respond to JavaScript and send the page param with your Ajax request

Displaying record count - Ruby on Rails - Ajax

I am new to rails so sorry if sometimes I don't make much sense. Here is what I am trying to do. I am trying to build a vote system. So next to a blog post there is a link that says 'vote' (will probably say like later). So far I have working: when the vote button is clicked, a value of '1' is delivered to the vote table and then that particular posts vote records display beneath the vote via AJAX (I copied a comment functionality). Instead of rendering all the number '1's below, I want it to render the updated count.
My vote table has the columns 'vote' and 'post_id' that are successfully being entered. My thinking was that I could just change my partial template to do this. Here is the code:
votes_controller:
class VotesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#vote = #post.votes.create!(params[:vote])
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
def count
#post = Post.find(params[:post_id])
#vote = calculate :count
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
Here is the page where is is showing, /posts/show.html.erb:
<div id="backto"<%= link_to 'Back to all BattleCries', posts_path %></div>
<%= render :partial => #post %><br/>
<p5>Add a Comment</p5>
<div id="belt">
<div id="belttext">
<% remote_form_for [#post, Comment.new] do |f| %>
<p>
<%= f.text_area ( :body, :class => "commentarea") %>
</p>
<%= f.submit "Add Comment"%>
<% end %>
</div>
<div id="beltbottom">
</div>
</div><br/>
<br/><p5>Comment Stream </p5>
<div id="comments">
<%= render :partial => #post.comments %>
</div>
<p>
<% remote_form_for [#post, Vote.new] do |f| %>
<p>
<%= f.hidden_field :vote, :value => '1' %>
</p>
<%= f.submit "Vote" %>
<% end %>
<div id="vote">
<div id="votes">
<%= render :partial => #post.votes %>
</div>
</div>
</p>
Here is the :partial, /votes/_vote.html.erb: (this is where I thought I would just need to change it to vote.count, or post.count or something but can't get it to work).
<% div_for vote do %>
<%= h(vote.vote) %>
<% end %>
Here is the /votes/create.js.rjs file:
page.insert_html :bottom, :votes, :partial => #vote
page[#vote].visual_effect :highlight
I hope that all makes sense.
I think it's repeating because your .rjs is "inserting at the bottom" of the div instead of "replacing" ... you probably want page.replace_html
It would be better to have a DIV or SPAN tag that contains the number of votes for a post ... then have your .rjs file update the DIV's inner_html with the number of votes (which would be #post.votes.count) ... I don't think you really need a partial.
You probably want:
<%= #post.votes.count %>
You also probably want to use replace instead of insert_html - does that make sense? Insert is just adding more elements to the DOM whereas replace will replace the element.

Resources