営業からプログラマに転身して早2ヶ月。会社の研修の最終章で上司から「インスタを作ってみようか」と言われRailsチュートリアルを実施。
PCのUIを元にチュートリアルを拡張してインスタもどきを作ってみた。
そのうち、次の記事を参考にしたがAjaxがなかなか思うように動かなくて少し苦戦した。
参考:Ajaxを用いた動的なコメント投稿・削除機能の実装で学ぶRuby on Rails
最終的に自分の望む動作になったので、その方法について書こうと思う。
望む動作
・コメントに投稿して右側のコメントアイコンを押すと投稿される
・投稿するとコメントがコメントフォームの上に追加される(画像ではaaaaaaaの部分)
・削除を押すと対象のコメントだけが削除される
・ユーザー名をクリックするとその人のページに飛ぶ
苦戦したところ
・マイクロポストAにコメントを投稿すると、コメントは投稿されるがページ更新するとコメントが消えた
・マイクロポストBにコメントを投稿するとマイクロポストBにはコメントが投稿されず、マイクロポストAにBのコメントが飛んでいた
・削除を押してもコメントは消えず、ページ更新することでコメントが削除された(Ajaxがうまく動かなかった)
なお、スキーマ、モデル、ルーティング、コメントコントローラは参考URL通りのため省略する。
動作がうまくいかない原因
詳細は後ほど述べるが、javascriptの参照先のidが固定のidになっていたことが原因。
→各マイクロポストのidを与えないといけなかった。
また、コメントの表示パーシャル呼び出し時に、パーシャルに渡す情報にmicropostのidが含まれておらず、Undefined Methodの原因となった。
コントローラ
def post
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.page(params[:page])
@comment = Comment.new
@comments = @micropost.comments
else
redirect_to login_url
end
end
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.page(params[:page])
@comment = Comment.new
@comments = @micropost.comments
else
redirect_to login_url
end
end
end
RailsでのAjaxを使ったコメント投稿はいろいろ先駆者がいたものの、どの方もmicropostコントローラのshowアクションに表示機能をつけていて同じ文が書けないことで混乱した。
具体的にmicropostコントローラでは
def show
@micropost = Micropost.find(params[:id])
end
のように書いていたが、私の場合はstatic_pages_controllerに表示をさせていたのでmicropostの取得の記述が異なりパニクった。
投稿のビュー
マイクロポストパーシャル
<div class="comment">
<div class="comment_area" id="comment_area">
<%= render 'comments/index', comments: micropost.comments, micropost: micropost %>
</div>
</div><!--.comment-->
参考記事やその他の情報では一つのマイクロポスト(あるいはポスト)に対してコメント機能をつけることを想定しているようで、各マイクロポストのidが不要だった。ところが複数のマイクロポストを表示し、それぞれに対応したコメントを紐付けることが前提の今回ではmicropostの情報を渡さねばならずここに気づくのに時間がかかった。
コメント表示パーシャル
<% comments.each do |comment| %>
<% unless comment.id.nil? %>
<div class="one_come">
<%= link_to "#{comment.user.name}", comment.user, class: "cmntname" %>
<span class="body">
<%= comment.body %>
</span>
<% if comment.user == current_user %>
<span class="delete_cmnt">
<%= link_to " ×",micropost_comment_path(comment.micropost_id, comment.id), method: :delete, remote: true, class: "cmnt_delete" %>
</span><!--.delete_cmnt-->
<% end %>
</div><!--.one_come-->
<% end %>
<% end %>
基本的には参考記事と同じであるが、できる限りインスタを模倣するためコメントの前にユーザー名を入れた。
冒頭にも書いたようにユーザー名を書くとユーザーページに遷移するようにしている。
コメント投稿パーシャル
<%= form_with(model: [micropost, comment]) do |f| %>
<div class="area_cmnt">
<ul>
<li>
<%= f.text_area :body, placeholder: "コメントを入力", class: "comment_text", id: "text_area" %>
</li>
<li>
<%= button_tag( class: "btn btn-default btn-sm") do %>
<%= content_tag :div, "", class: "glyphicon glyphicon-comment"%>
<% end %>
</li>
</ul>
</div>
<% end %>
コメントも参考記事とほぼ同じであるが、投稿ボタンをコメントアイコンに変えた。
本当はクリックでも投稿できるようにしたいがフィードのポスト分だけコメントが投稿されるバグがあり保留としている。
jsファイル
$("#micropost-<%= @micropost.id %> .comment_area").html("<%=j(render 'index', {comments: @comment.micropost.comments} ) %>");
$("textarea").val('');
ここが一番難しかった場所である。javascriptをやっている方にしては当たり前のことだとは思うが、Railsも初めてならjsも初めてな自分にとっては本当にちんぷんかんぷんだった。
当初、参考記事のようにid部分をmicropostフォームに作った固有のidにしていたが、冒頭のようなエラーが起きてしまった。
そこでもう一度チュートリアルを見直したがわからず、結果的に先に実装したファボ機能を参考にしたところうまく解決した。
削除についても同様であるが、削除後の挙動についてmicropostの情報を渡していない部分でかなり悩んだ。
終わりに
インスタはPythonで作られているがRailsでもやろうと思えば近いかたちに持っていくことができるということがわかった。
ほぼチュートリアルどおりだったが、新しいルーティングを構成したり、ビューを作ったりしていくなかで理解が深まったと思う。
今は仕事でplayframeworkをやっているがRailsの影響を受けているだけあって構成が似ていて、当該研修をやってよかったと思う。
一方で、このような初歩的なことで躓いてしまったがそれを解決しながら、しかも自分がカスタムした結果が画面ですぐに分かるのが楽しかった。自分はこういうことがしたかったので1日8時間も独学させてもらえたことが嬉しかった。
まだまだ未熟ではあるがどんどんのめり込んで有効な記事を書いていきたいと思う。
これもQiitaの先輩方の経験・知恵があってのことだと思う。ありがとうございます。