Authenticating with Acts As Follower and Devise - javascript

I'm using Devise with Acts As Follower. The setup is simple where users can follow celebrities. What I'd like to do is only allow signed in users to follow/unfollow celebrities. If a non-registered user clicks the follow button I'd like them to be redirected to the registration screen. I'm aware of Devise's before_action :authenticate_user! but having a hard time figuring out how to trigger this as a clickable event for my follow/unfollow actions in my celebrities controller.
Where my current code falls short is that it only works if the current_user is signed in. If not a receive the error: undefined method 'following?' for nil:NilClass. I'd like to know know how I could fix this and if there's anything that could be improved as my rails code and javascript's toggle functionality.
Celebrity Show View
<% if current_user.following?(#celebrity) %>
<%= link_to "Unfollow", unfollow_celebrity_path, method: :post, class: "unfollow", remote: true %>
<% else %>
<%= link_to "Follow", follow_celebrity_path, method: :post, class: "follow", remote: true %>
<% end %>
<%= render _toggle.html.erb %>
Toggle Button Partial
<script>
$(function() {
$('a').click(function() {
if ($(this).text() == "Unfollow") {
$(this).html("<%= escape_javascript( link_to 'Follow', follow_celebrity_path, method: :post, remote: true) %>").addClass('follow')
} else {
$(this).html("<%= escape_javascript( link_to 'Unfollow', unfollow_celebrity_path, method: :post, remote: true) %>").addClass('unfollow')
}
});
});
</script>
Celebrities Controller
...
def follow
#celebrity = Celebrity.find(params[:id])
if current_user
current_user.follow(#celebrity)
end
end
def unfollow
#celebrity = Celebrity.find(params[:id])
if current_user
current_user.stop_following(#celebrity)
end
end
Routes
resources :celebrities do
member do
post :follow
post :unfollow
end
end

This should fix it (Update your if condition):
<% if current_user.present? && current_user.following?(#celebrity) %>
Regarding User login redirect, this should work(sorry I don't have time to test):
<script>
$(function() {
$('a').click(function() {
if ($(this).text() == "Unfollow") {
$(this).html("<%= escape_javascript( link_to 'Follow', follow_artist_path, method: :post, remote: true) %>").addClass('follow').removeClass('unfollow');
} else {
$(this).html("<%= escape_javascript( link_to 'Unfollow', unfollow_artist_path, method: :post, remote: true) %>").addClass('unfollow').removeClass('follow');
}
})
$(".follow,.unfollow").bind("ajax:error", function(data, xhr, status, error) {
if (xhr.status == 401) {
window.location.replace("<%= new_user_session_path %>");
}
});
});
</script>

Related

Rails destroy js.erb

Can you tell me please, how I could remove object before destroy method will be end. When I use next pattern, removing object happen when photo was delete, but it take 1 or 3 or more seconds.
_form(edit action)
<% listing.photos.each do |photo|%>
<%= image_tag photo.image.thumb, class: 'thumbnail', id: "test"%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
destroy.js.erb
$('#test').remove();
How I can use this pattern
_form:
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
Destroy.js.erb:
$('#edit_image_<%= #photo.id %>').remove();
If you want to remove the image from the DOM before its real destroying on server to avoid delay you can apply event.preventDefault() on the 'remove' button click.
This will allow you to rewrite the normal behavior of the 'remove' button.
Take a look at this example about performing some UI actions before the original event and then triggering it.
Also please notice that it's not a good idea to remove something from UI without being sure it's already removed in general. It's not clear enough for the user. So, maybe it would be better to hide the image at first and in case there will be a server error while destroying it you'll show it again and display some instructive message also.
UPD
Considering following markup
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail' %>
<%= link_to "remove", "#", class: 'remove btn btn-primary', data: { id: photo.id, url: photo_path(photo) } %>
</div>
another option is to rewrite remote: true with separate jQuery.ajax() call.
$('.btn.remove').click(function () {
var $target = $(this).parents('#test_' + $(this).data('id'));
$.ajax({
url: $(this).data('url'),
method: 'DELETE',
beforeSend: function() {
$target.hide() # hiding corresponding image
},
error: function () {
$target.show() # revert hiding on error
console.log("Sorry! Couldn't remove image.") # log message
}
})
})
There is a much cleaner way to do it without a js.erb template:
<div class="photo">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'destroy btn btn-primary', method: :delete, data: { remote: true, type: 'json', confirm: 'Are you sure?' } %>
<div>
Now just setup an ajax handler:
// app/assets/javascripts/photos.js
$(document).on('ajax:success', '.photo .destroy.btn', function(){
$(this).parent('.photo').remove();
});
And set your controller to return the correct response code.
class PhotosController
def destroy
#photo = Photo.find(params[:id])
#photo.destroy!
respond_to do |format|
format.json do
head :no_content
end
end
end
end
This keeps your client side logic in app/assets/javascripts where it can be cached and minified instead of spreading it around in a bunch of glorified script tags.

Why is the link_to submitting form twice?

I am trying to use JavaScript with jquery in rails to update a div without reloading the page. Its working but the comment is being created twice. I know something is wrong in the form partial but couldn't figure it out. Any help?
The following are the files -
_new_comment.html.erb
<%= form_for [:home, Comment.new], :url => home_comments_path, :html => {:id => "comment-new-" + post.id.to_s} do |f| %>
<div>
<%= f.text_area :message, :class => "message-area-" + post.id.to_s, :placeholder => "Add comment..." %>
<%= f.hidden_field :post_id, :value => post.id %>
<span class="new-comment"><%= link_to 'Add', '#', remote: true, :onclick => "$('#comment-new-#{post.id}').submit()" %></span>
</div>
<% end %>
comments_controller.rb
def create
#comment = Comment.new(comment_params)
#comment.user_id = current_user.id
if #comment.save
flash[:notice] = 'Comment added sucessfully'
respond_to do |format|
format.html { redirect_to home_posts_url }
format.js
end
end
end
create.js.erb
$("#comment-new-83").before('<div class="notice"><%= escape_javascript(flash[:notice]) %></div>');
$("#comment-new-83")[0].reset();
home.js
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
})
$(document).ready(function() {
$("#comment-new-83").submit(function() {
$.post($(this).attr("action"), $(this).serialize(), null, "script");
return false;
})
})
form_for will automatically post to the controller. You need not submit the form explicitly using jquery.
Reference

Extra Javascript code posting under Form after Ajax Rails form submit ( \n\n...etc)

I am following section 4 (Server Side Concerns) to set up ajax on a page. I've copied the tutorial text completely (replacing the model names with my own) and it creates and saves my "Participants" record and refreshes the partial....but it posts this weird code below the just submitted form:
$("
\n\n Helper:sampleemail#sample.com \n<\/li>\n\n").appendTo("#participants");
This seems to be some sort of weird mash-up of my partial information and my creat.js. It's not really an error...just extra code.
Here's my code
class ParticipantsController < ApplicationController
def new
#participant = Participant.new
#participants = #participants.recently_updated
end
def create
#participant = Participant.new(participant_params)
respond_to do |format|
if #participant.save
format.html { redirect_to #participant, notice: 'Helper Invited!' }
format.js {}
format.json { render json: #participant, status: :created, location: #participant }
else
format.html { render action: "new" }
format.json { render json: #participant.errors, status: :unprocessable_entity }
end
end
end
_form.html.erb
<ul id="participants">
<%= render #participants %>
</ul>
<%= form_for(#participant, remote: true) do |f| %>
  <%= f.label :email %><br>
  <%= f.email_field :email %>
<%= f.submit 'SUBMIT' %>
<script>
$(document).ready(function() {
return $("#new_participant").on("ajax:success", function(e, data, status, xhr) {
return $("#new_participant").append(xhr.responseText);
}).on("ajax:error", function(e, xhr, status, error) {
return $("#new_participant").append("<p>Oops. Please Try again.</p>");
});
});
</script>
<script>
$(function() {
return $("a[data-remote]").on("ajax:success", function(e, data, status, xhr) {
return alert("The helper has been removed and notified.");
});
});
</script>
_participant.html.erb
<li >
<%= participant.email %> <%= link_to participant, remote: true, method: :delete, data: { confirm: 'Are you sure?' } do %>REMOVE<% end %>
</li>
create.js.erb
$("<%= escape_javascript(render #participant) %>").appendTo("#participants");
destroy.js.erb
$('#participants').html("<%= j (render #participants) %>");
It seems like you are both responding with a js.erb file and listening for the ajax:success event. You should only need to do one. Right now the ajax:success listener is appending the response to the form and the response is javascript code. Remove that listener and you should get the desired results.

Ruby on Rails Like button with image instead of text

I am currently working with the Acts_As_Votable gem in Ruby to use a Like link on each of my posts so that users can vote. I found a great answer on here that helped be do exactly that but I'm trying to figure out how I can use a heart icon image instead of the Like/Dislike text. I want the image to update without refreshing, just like the text links do but can't figure out the logic. Here is the current code I am using:
controllers/prides_controller.rb
def like
#pride = Pride.find(params[:id])
#pride.liked_by current_user
if request.xhr?
render json: { count: #pride.get_likes.size, id: params[:id] }
else
redirect_to #pride
end
end
def dislike
#pride = Pride.find(params[:id])
#pride.disliked_by current_user
if request.xhr?
render json: { count: #pride.get_likes.size, id: params[:id] }
else
redirect_to #pride
end
end
prides/show.html.erb
<div class="single-heart-this">
<% if current_user.liked? #pride %>
<span class="heart-icon-loved">
<%= link_to "Dislike", dislike_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Like', toggle_href: like_pride_path(#pride), id: #pride.id } %></span>
<% else %>
<span class="heart-icon">
<%= link_to "Like", like_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Dislike', toggle_href: dislike_pride_path(#pride), id: #pride.id } %></span>
<% end %>
<span class="heart-no" data-id="<%= #pride.id %>"><%= #pride.get_likes.size %></span>
<% end %>
</div>
javascript/pride.js.erb
# Rails creates this event, when the link_to(remote: true)
# successfully executes
$(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
# the `data` parameter is the decoded JSON object
$(".heart-no[data-id=#{data.id}]").text data.count
return
javascript/prides.js.coffee
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
$(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
# update counter
$(".heart-no[data-id=#{data.id}]").text data.count
# toggle links
$("a.vote[data-id=#{data.id}]").each ->
$a = $(this)
href = $a.attr 'href'
text = '$a.text()'
$a.text($a.data('toggle-text')).attr 'href', $a.data('toggle-href')
$a.data('toggle-text', 'text').data 'toggle-href', href
return
return
Thanks for your help.
Try this?
Add: class: "Change_me" to your link
<%= link_to image_tag("Dislike.png"), dislike_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_href: like_pride_path(#pride), id: #pride.id }, class: "change_me" %>
src: link_to image_tag with inner text or html in rails
In your coffeescript, change the image src
$(".change_me").attr 'src', '/like.png'
src: Rails 3.1 CoffeeScript/JQuery - How to change the source of an image in a div?

Javascript voting acts_as_votable

I have a rails app, in which my Posts model has Comments and the comments are votable. I'm using acts_as_votable.
I currently have the voting on the comments working. Now I'm trying to implement some javascript so that the page does not have to refresh every time someone votes on a comment, so that the vote goes through.
Here is what I had before(which was working):
In my comments controller:
def upvote_post_comment
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back}
end
end
And in my view:
<% if user_signed_in? && current_user != comment.user && !(current_user.voted_for? comment) %>
<%= link_to image_tag(‘vote.png'), like_post_comment_path(#post, comment), method: :put %> <a>
<%= "#{comment.votes.size}"%></a>
<% elsif user_signed_in? && (current_user = comment.user) %>
<%= image_tag(‘voted.png')%><a><%= "#{comment.votes.size}"%></a>
<% else %>
<%= image_tag(‘voted.png')%><a><%= "#{comment.votes.size}"%></a>
<% end %>
And this is what I now have:
In my comments controller:
def upvote_post_comment
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.liked_by current_user
respond_to do |format|
format.html {redirect_to :back }
format.json { render json: { count: #comment.liked_count } }
end
end
And in my view:
<% if user_signed_in? && current_user != comment.user && !(current_user.voted_for? comment) %>
<%= link_to image_tag(‘vote.png'), like_post_comment_path(#post, comment), method: :put, class: 'vote', remote: true %>
<a><%= "#{comment.votes.size}"%></a>
<script>
$('.vote')
.on('ajax:send', function () { $(this).addClass('loading'); })
.on('ajax:complete', function () { $(this).removeClass('loading'); })
.on('ajax:error', function () { $(this).after('<div class="error">There was an issue.</div>'); })
.on('ajax:success', function (data) { $(this).html(data.count); });
</script>
<% elsif user_signed_in? && (current_user = comment.user) %>
<%= image_tag(‘voted.png')%><a><%= "#{comment.votes.size}"%></a>
<% else %>
<%= image_tag(‘voted.png')%><a><%= "#{comment.votes.size}"%></a>
<% end %>
This shows me the error message: "There was an issue"
And when I refresh the page, I see that the vote went through and I see this in my terminal:
Started PUT “/1/comments/1/like" for 127.0.0.1 at 2014-04-06 18:54:38 -0400
Processing by CommentsController#upvote_post_comment as JS
Parameters: {"post_id"=>”1”, "id"=>”1”}
How do I get the voting to work via Javascript? So that the vote goes through, the vote count updates and the vote icon updates to voted.png instead of vote.png?
Your log says the request is formatted as JS.
Processing by CommentsController#upvote_post_comment as JS
Add data: { type: :json } to the link_to method to request a JSON format like so,
<%= link_to image_tag('vote.png'), like_post_comment_path(#post, comment), method: :put, class: 'vote', remote: true, data: { type: :json } %>
This will tell the controller you want a JSON response not a Javascript response.
Edit - updates from the comments.
Update controller to use,
format.json { render json: { count: #comment.likes.size } }
Update JS to use,
.on('ajax:success', function(e, data, status, xhr) { $(this).html(data.count); });

Categories

Resources