最近两个月我跟着老师学习画素描,有时候把基本的形状和明暗关系找到之后就不想往下画了,老师说不行,你不深入刻划一下局部,真正巧妙的东西还是领悟不到。咱们还是来看这个评论框,现在如果提交评论,那页面会整个刷新,于是会跳到页顶,用户体验不好。真正热爱写代码的你,当然是希望再进一步刻划一下,所以这集来把评论功能 ajax 化

更改前端 html

前端最关键的一步就是添加 remote: true 。到 _comment_box.html.erb 中

- <%= form_for(Comment.new(:issue_id => issue.id, :user_id => current_user.id)) do |f| %>
+ <%= form_for(Comment.new(:issue_id => issue.id, :user_id => current_user.id), remote: true) do |f| %>

这样转换成的 html 其实变化不大,就是多了 data-remote="true" 这几个字,但是注意在 application.js 文件中 有 require jquery_ujs,真正的魔术都在 jquery_ujs 这个 js 文件中呢,里面的代码会把这里的表单提交自动变成一次 ajax 提交。具体细节不用管,真正要关心的就是后台 log 中的变化。如果出现 Processing by IssuesController#show as JS (非 ajax 请求是请求 html) 证明前端要做的修改就弄好了。

修改后端

下面到 comments_controller.rb 里面来提供 JS 格式的响应。

def create
_ c = Comment.new(comment_params)
- c.save
- redirect_to c.issue
+ @comment = Comment.new(comment_params)
+ @comment.save
+ respond_to do |format|
+   format.js
+ end
end

然后创建 app/views/comments/create.js 注意这样位置不能敲错,不然 rails 就找不着这个文件了。

先在里面写个 alert("hello"); 这样在前端提交一下评论,就可以看到后端给发送过来 create.js 的内容,并在浏览器里执行了。好,这就是基本流程,挺简单吧。

所以现在就要在 create.js 添加适当的内容,来把新发布的评论添加到页面中来。大概这些内容:

$(".replies").append("<%= j render(partial: 'shared/comment', locals: {c: @comment}) %>");

注意: append() 括号的内容,外面用双引号,里面单引号,不要同时都用单引号或是双引号。 另外,这里的 jescape_javascript 的简写形式。目的是把 partial 中的引号和回车进行转义,否则 render 出来的内容直接放到 create.js 中就会造成 js 的代码错误了。

需要一个 shared/_comment.html.erb 。但是现在没有,所以可以来重构一下 _comment_list.html.erb 。首先要把它重命名为 _comment.html.erb ,然后里面的内容做出下面的调整。

- <% comments.each do |c| %>
- <% end %>

这样 issues/show.html.erb 中的代码也要调整。把原有的使用 shared/_comment_list.html.erb 的那一行代码改为下面这三行

<% @comments.each do |c| %>
  <%= render partial: 'shared/comment', locals: {c: c} %>
<% end %>

然后再把

<%= render partial: 'shared/comment_box', locals: {issue: @issue} %>

往下移动一行,也就是把它从 class 名为 replies 的 div 中拿出来,配合 create.js 中的 $(".replies").append()

发一条评论,ajax 生效了,哈哈。但是评论框没有被清空,所以到 create.js 里再来添加一行

$(".reply textarea").val('');

devtools 中的调试技巧

如果不小心敲错了 ruby 代码,比如 create.js 中把 render 写成了 rennder 那么点击”发布评论“按钮是看不到报错信息的。 可以打开 devtools -> console ,这里可以看到一个 500 的错误。具体的错误内容可以到 network 下面,再次提交以下评论,就能看到了。

但是如果是 js 代码敲错了,比如 append 敲成了 apppend,这样是没有任何报错信息的,但是依然可以到 network 下面,再次 提交评论,把返回的 js 代码自己放到 console 中执行一下,这样到底哪里出错就比较明显了。