AJAX 渲染局部视图

3
我试图实现reddit风格的投票按钮,虽然不太优雅,但我认为这是最直接的方式。我有没有使用AJAX就能运行的代码,但我希望用户能够点击投票按钮并相应地更改它们,而不重新加载页面(因此需要使用AJAX)。
当我尝试删除所点击的投票按钮并替换为相应的“突出显示”的投票按钮时,问题就出现了。我之所以要替换投票按钮是因为我需要将其更改为一个可以取消投票的按钮。
(我已经简化了此过程,只处理了上投票,因为下投票具有相同的机制)。这是我的视图:
  <% @posts.each do |post| %>
    <%= post.content %>
    <% if current_user.voted_as_when_voted_for post %>
      <%= render 'upvoted', post: post %>
    <% else %>
      <%= render 'upvote', post: post %>
    <% end %>
  <% end %>

投票过该帖子的用户可以使用voted_as_when_voted_for方法,如果他们已经点赞,则返回true。 下面是点赞和已点赞的表单:
<%= link_to image_tag('upvoted.png', size: '18'), remove_like_post_path(post), method: :put, remote: true, id: "upvoted#{post.id}" %>

<%= link_to image_tag('upvote.png', size: '18'), like_post_path(post), method: :put, remote: true, id: "upvote#{post.id}" %>

这是我的控制器:

def upvote
  @post = Post.find(params[:id])
  @post.liked_by current_user
  respond_to do |format|
    format.html { redirect_to @post.parent }
    format.js   {}
  end
end

def remove_upvote
  @post = Post.find(params[:id])
  @post.unliked_by current_user
  respond_to do |format|
    format.html { redirect_to @post.parent }
    format.js   {}
  end
end

upvote.js.erb: and upvoted.js.erb:

$('#upvote<%= @post.id %>').remove().append('<%= j render("upvoted") %>');

$('#upvoted<%= @post.id %>').remove().append('<%= j render("upvote") %>');

使用这些文件,我得到了以下控制台错误:

PUT http://localhost:3000/posts/96/like 500 (Internal Server Error) 

NameError in Posts#upvote

undefined local variable or method `post' for #<#<Class:0x007fde1836cbf0>:0x007fde18c00af0>

_upvote.html.erb and _upvoted.html.erb

<%= link_to image_tag('upvote.png', size: '18'), like_post_path(post), method: :put, remote: true, id: "upvote#{post.id}" %>

<%= link_to image_tag('upvoted.png', size: '18'), like_post_path(post), method: :put, remote: true, id: "upvoted#{post.id}" %>

我也尝试了以下类似的操作,基于下面链接中的方法:
$('#upvote<%= @post.id %>').remove().after('<%= j render "upvoted", post: @post %>');

当我使用那段代码时,成功删除了点赞,但是点赞表单没有添加上。因此似乎我需要使用实例变量,但仍然有些问题。
我错过了什么吗?
这个人似乎有类似的问题:Rails Ajax Render Partial

开发者控制台有任何错误吗?你能在js.erb文件中弹出警报吗? - Mandeep
我在生成错误的代码中添加了控制台日志。我是Rails的新手,对这些日志不是很熟悉,如果您想从中看到更多内容,请告诉我。 - hodale
尝试执行以下代码:$('#upvote<%= @post.id %>').remove().after('<%= j render partial: "upvoted", locals: {post: @post} %>'); - Mandeep
3
可能还有其他问题,但就 JavaScript 而言,你正在对匹配的元素调用 .remove()。然后试图将其附加到一个不再存在的元素上。 - veritas1
@veritas1,没错,我是先添加了链接,然后再移除它。 - hodale
2个回答

0

好的,所以导致ajax失败的真正罪魁祸首是使用.remove().after。我的理论是,如果我删除了元素,那么你就不能在它后面添加任何东西。因此,这个修复解决了问题:

$('#upvote<%= @post.id %>').after('<%= j render "upvoted", post: @post %>');
$('#upvote<%= @post.id %>').remove();

也许不是最复杂的方法,但它能够正常工作!注意:我不想使用 .hide().after(),因为我不想让很多隐藏的链接潜伏在后面。


0

DRY

看起来对我来说不是很DRY

通过更好地利用您的HTTP动词 - 通过定义delete路由(用于downvote)和post路由(用于upvote),您可以删除许多重复的代码:

#config/routes.rb
resources :posts do
   match :vote, via: [:post, :delete] #-> domain.com/posts/14/vote
end

#app/controllers/posts_controller.rb
Class PostsController < ActiveRecord::Base
    respond_to :js, :html
    def vote
       @post = Post.find params[:id]

       if request.post? 
          @post.liked_by current_user
       elsif request.delete?
          @post.unliked_by current_user
       end

       respond_with @post
    end
end

视图

这将允许您创建以下JS和视图:

#app/views/posts/vote.js.erb
$("<%= 'upvote##{@post.id}' %>").html('<%= j render "vote", locals: { post: @post } %>');

-

#app/views/posts/_vote.html.erb
image = current_user.voted_as_when_voted_for(post) ? "downvote.png" : "upvote.png"
method = current_user.voted_as_when_voted_for(post) ? :delete : :post

<%= link_to image_tag(image, size: '18'), post_vote_path(post), method: method, remote: true, id: "upvote#{post.id}" %>

-

#app/views/posts/show.html.erb
<% @posts.each do |post| %>
    <%= post.content %>
    <%= render 'vote', post: post %>
<% end %>

修复

在查看您的代码后,我会查看错误以获取有关问题可能是什么的提示:

未定义的本地变量或方法 `post'

问题似乎在您的 upvote 操作中被调用,该操作将具有以下 partial upvoted

$('#upvoted<%= @post.id %>').remove().append('<%= j render("upvote") %>');

问题在于这个局部需要 post 本地变量。所以问题在于你没有通过 locals 参数将 @post 传递给你的 partial
我上面的代码解决了这个问题,但为了解决它,你应该只使用 locals: { post: @post }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接