Rails upvote/downvote с использованием Ajax и acts_as_votable

Все еще довольно новый для Rails. Я использовал камень act_as_votable для создания кнопок upvote и upvote, позволяющих пользователям шутить upvote/downvote, но я не могу изменить их с upvote/downvote (и наоборот) и обновлять счетчик каждый раз, когда они нажимают, не обновляя страницу. Я попытался следовать другим подобным ответам без удачи. Вот что я пытался реализовать.

Шутка Модель

acts_as_votable 

Модель пользователя

acts_as voter

Routes.rb это

resources :jokes do
member do
  get "like" => "jokes#upvote"
  get "unlike" => "jokes#downvote"
end
end

Шутки Контроллер есть

#upvote from user
def upvote
    @joke = Joke.find(params[:id])
    @joke.upvote_from current_user
    respond_to do |format|
        format.html { redirect_to :back }
        format.js { render :layout => false }
    end
end

#downvote from user
def downvote
    @joke = Joke.find(params[:id])
    @joke.downvote_from current_user
    respond_to do |format|
        format.html { redirect_to :back }
        format.js { render :layout => false }
    end
end

Мой взгляд:

<div class="votes">
<%= link_to like_joke_path(@joke), method: :get, remote: true, class:     'like_joke' do %>
    <button type="button" class="btn btn-info" aria-label="Left Align">
        <i class="fa fa-arrow-up"></i>
        <span class="badge vote_count"><%= @joke.get_upvotes.size %></span>
    </button>
<% end %>

<%= link_to unlike_joke_path(@joke), method: :get, remote: true, class: 'unlike_joke' do %>
    <button type="button" class="btn btn-info" aria-label="Left Align">
        <i class="fa fa-arrow-down"></i>
        <span class="badge voute_count"><%= @joke.get_downvotes.size %></span>
    </button>
<% end %>
</div>

like.js.erb:

$('.like_joke').bind('ajax:success', function(){
   $(this).parent().parent().find('.vote_count').html('<%= escape_javascript     @joke.votes_for.size.to_s %>');
   $(this).closest('.like_joke').hide();
   $(this).closest('.votes').html(' <%= link_to "Unlike",  unlike_joke_path(@joke), remote: true, method: :get, class: 'unlike_joke' %>');
});

и, наконец, в отличие от.js.erb:

$('.unlike_joke').bind('ajax:success', function(){
   $(this).parent().parent().find('.vote_count').html('<%= escape_javascript @joke.votes_for.size.to_s %>');
   $(this).closest('.unlike_joke').hide();
   $(this).closest('.votes').html(' <%= link_to "Like", like_joke_path(@joke), remote: true, method: :get, class: 'like_joke' %>');
});

Вот отрендеренный HTML:

<div class="votes">
<a class="like_joke" data-remote="true" data-method="get" href="/jokes/11-cat-joke-title/like">
    <button type="button" class="btn btn-info" aria-label="Left Align">
        <i class="fa fa-arrow-up"></i>
        <span class="badge vote_count">0</span>
    </button>
</a>
<a class="unlike_joke" data-remote="true" data-method="get" href="/jokes/11-cat-joke-title/unlike">
    <button type="button" class="btn btn-info" aria-label="Left Align">
        <i class="fa fa-arrow-down"></i>
        <span class="badge voute_count">1</span>
     </button>
</a></div>

Когда я смотрю на консоль js, я получаю внутреннюю ошибку сервера по адресу jquery.js? Body=1:9665, что равно xhr.send( ( options.hasContent && options.data) || null);

Кажется, что я близко, но я думаю, что что-то напутало в моих взглядах и сомнениях. Может кто-нибудь показать мне, чего мне не хватает?

2 ответа

Решение

Если вы посмотрите на код, который вы предоставили... код раскрашен для того, что находится внутри и снаружи строки... и вы заметите эту строку здесь:

 $(this).closest('.votes').html(' <%= link_to "Unlike",  unlike_joke_path(@joke), remote: true, method: :get, class: 'unlike_joke' %>');

окрашивается так, что части кода находятся внутри строки... но части нет (прокрутите вправо, чтобы увидеть его, он ближе к концу).

Я не знаю наверняка, что это ваша ошибка, но вы, вероятно, должны рассмотреть возможность изменения этого на:

 $(this).closest('.votes').html(' <%= link_to "Unlike",  unlike_joke_path(@joke), remote: true, method: :get, class: "unlike_joke" %>');

лишь бы прояснить неясность. (также проверьте другие строки для подобных проблем)


Хорошо, я думаю, что проблема заключается в этом. как только вы нажали как... вы переходите к действию для симпатии (что, кстати, вам, вероятно, следует называть "лайк" вместо "upvote" для согласованности)> Затем вы рендерите like.js.

в этот момент вы хотите, чтобы js фактически обновил счетчик лайков и т. д. У вас уже есть javascript, и вы просто хотите запустить его сразу. Вам не нужно связывать это с успехом AJAX... вы уже добились успеха. Таким образом, ваш like.js должен просто содержать пустой javascript для фактического обновления экрана прямо сейчас, например:

var like_joke = $('.unlike_joke');
like_joke.parent().parent().find('.vote_count').html('<%= escape_javascript     @joke.votes_for.size.to_s %>');
like_joke.closest('.like_joke').hide();
like_joke.closest('.votes').html(' <%= link_to "Unlike",  unlike_joke_path(@joke), remote: true, method: :get, class: 'unlike_joke' %>');

Примечание: не проверено и могут быть ошибки, но вы поняли идею. Не помещайте функцию, которая будет вызываться при успешном выполнении ajax, или она будет сидеть там в ожидании какого-то другого успеха. Успех уже произошел... теперь просто иди и делай дело:)

Также обратите внимание: здесь есть опечатка:

   <span class="badge voute_count"><%= @joke.get_downvotes.size %></span>

должно быть

   <span class="badge vote_count"><%= @joke.get_downvotes.size %></span>

Лучший способ...

#config/routes.rb
resources :jokes do
  match :vote, via: [:post, :delete], on: :member
end

#app/controllers/jokes_controller.rb
class JokesController < ApplicationController
   def vote
       @joke = Joke.find(params[:id])
       if request.post?
           @joke.upvote_from current_user
       elsif request.delete?
           @joke.downvote_from current_user
       end
       respond_to do |format|
          format.html { redirect_to :back }
          format.js
       end
   end
end

Сделать один partial Посмотреть:

#app/views/jokes/_vote.html.erb
<% voted   = current_user.voted_for? joke %>
<% styling = voted ? "" : "un" %>
<% arrow   = voted ? "up" : "down" %>
<% method  = voted ? :delete : :post %>

<%= link_to vote_joke_path(joke), method: method, remote: true, class: '#{styling}like_joke', id: joke.id do %>
    <button type="button" class="btn btn-info" aria-label="Left Align">
        <i class="fa fa-arrow-<%= arrow %>"></i>
        <span class="badge vote_count"><%= joke.get_upvotes.size %></span>
    </button>
<% end %>

#app/views/jokes/show.html.erb
<%= render partial: "vote", locals: { joke: @joke } %>

Таким образом, когда вы вызываете vote.js.erb от твоего JokesController, вы сможете использовать следующее:

#app/views/jokes/vote.js.erb
$("<%=j @joke.id %>").html("<%=j render partial: "jokes/vote", locals: { joke: @joke } %>");
Другие вопросы по тегам