4月20日〜6月末(76期)までテックキャンプ エンジニア転職コースを受講し、現在IT企業へ転職活動中のまさひで(@john01tgmck)です。
テックキャンプ では、基礎〜応用カリキュラムが終わった後に、個人アプリ開発を行います。
-
-
参考質問NG!?テックキャンプ の個人アプリは孤独との戦い
期間は1週間!?ポートフォリオの必要性と作成難易度をまとめています。できるだけ早くカリキュラムを終わらせる事がオススメ
続きを見る
作成したWEBアプリ 公園を共有して親子の絆を深めよう『Park Connect』
非同期通信と自動更新を使用して、チャットっぽく見えるようになっています。
ただ、レスポンシブ対応していなかったり、応用カリキュラムの転用になっていたので、Railsの復習も兼ねて新しくアプリを立ち上げました。
そこで、非同期通信がワンライナーで実装できる記事を見つけたので、つまづいた部分も解説していきます。
目次
完成したアプリ
環境
- Ruby 2.5.1
- Rails 5.2.4
- Mysql 14.14
- CSSフレームワーク:Bluma使用

アプリケーションの構造
テーブル構造
Schema.rb
create_table "comments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "content"
t.bigint "user_id"
t.bigint "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["post_id"], name: "index_comments_on_post_id"
t.index ["user_id"], name: "index_comments_on_user_id"
end
create_table "likes", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.bigint "post_id"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["post_id"], name: "index_likes_on_post_id"
t.index ["user_id"], name: "index_likes_on_user_id"
end
create_table "posts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.integer "user_id"
t.string "title"
t.text "body"
t.string "image_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.string "username", null: false
t.text "profile"
t.string "profile_image_id"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "comments", "posts"
add_foreign_key "comments", "users"
add_foreign_key "likes", "posts"
add_foreign_key "likes", "users"
ルーティング
resources :posts do
resources :comments, only: [:create, :destroy]
end
どの投稿に対するコメントなのかわかるようにネストさせます。
投稿ページ
views / posts / show.html.erb
<section class="hero is-success">
<div class="hero-body">
<div class="container">
<h1 class="title">
投稿詳細
</h1>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="columns is-centered">
<div class="column is-7">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<%= attachment_image_tag @post, :image %>
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">タイトル:<%= @post.title %></p>
</div>
</div>
<div class="content">
<table class="table is-narrow">
<tr>
<th>内容</th>
</tr>
<tr>
<td><%= simple_format @post.body %></td>
</tr>
<tr>
<td class="display-flex">
<div id="likes_buttons_<%= @post.id %>">
<%= render partial: 'likes/like', locals: { post: @post} %>
</div>
</td>
</tr>
</table>
<% if @post.user.id == current_user.id %>
<%= link_to "編集画面へ", edit_post_path(@post), class: "button is-success" %>
<% end %>
</div>
</div>
</div>
</div>
<div class="column is-4">
<article class="panel is-link">
<p class="panel-heading">
<%= @post.user.username %>さん
</p>
<div class="panel-block">
<p class="control">
一言:<%= @post.user.profile %>
</p>
</div>
<%= link_to user_path(@post.user), class: "panel-block" do %>
<span class="panel-icon">
<i class="fas fa-user" aria-hidden="true"></i>
</span>
<%= @post.user.username %> さんのページへ
<% end %>
</article>
<h2>コメント一覧</h2>
<div class="comments_index">
<div id='comments_area'>
<%= render partial: 'comments/index', locals: { comments: @comments } %>
</div>
</div>
<% if user_signed_in? %>
<%= render partial: 'comments/form', locals: { comment: @comment, post: @post } %>
<% end %>
</div>
</div>
</div>
</section>
views/posts/show.html.erbファイル
68行目(コメント一覧)〜76行目あたりがコメントを投稿したり、表示する箇所になります。
renderで切り出して、Ajaxで差し替えやすいようにします。
それより上のコードは今回のコメント非同期とは関係ないので、割愛します。
コントローラーの構造
controllers / posts_controller.rb
def show
@post = Post.find(params[:id])
@comment = Comment.new
@comments = @post.comments.order(created_at: :desc)
end
@postにポストテーブルのレコードを代入
@commentsに新しいコメントから表示できるように並べ替えています。
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.build(comment_params)
@comment.user_id = current_user.id
if @comment.save
render :index
end
end
def destroy
@comment = Comment.find(params[:id])
if @comment.destroy
render :index
end
end
private
def comment_params
params.require(:comment).permit(:content, :post_id, :user_id)
end
end
保存されると、views/comments/index.js.erbを表示します。(render :index)
フォーム構造
form_forを使用しているので、remote :trueを記載します。
この一文を記載するだけで、対応するJSファイルを探しに行ってくれます。*コメント一覧表示であれば(index.js.erb)
views / comments / _form.html.erb
<%= form_for [@post, @comment], remote: true do |f| %>
<div class="field">
<div class="control">
<%= f.text_area :content, class:"input", placeholder:"コメント" %>
<%= f.hidden_field :post_id, value: @post.id %>
</div>
</div>
<br>
<div class="field is-grouped">
<div class="control">
<%= f.submit 'コメントする', class:"button is-link form__submit" %>
</div>
</div>
<% end %>
JSファイル作成
Views / comments / index.js.erb
$("#comments_area").html("<%= j(render 'index', { comments: @comment }) %>")
$("textarea").val('')
posts/show.html.erbファイルのid="comments_area"を書き換える処理になりす。
2行目は投稿後にフォームを空にする処理です。

え!?こんな短くていいの??
テックキャンプ で行っていた非同期通信は、JavaScriptフォルダに〇〇.jsっていうファイルを作って、functionとかajax,done,failなどを書いていくものでした。

チャットアプリで使用していたAjax処理です。
半分くらいしか理解できていないので、ざっくり言うと、送信ボタン(submit)が押された時にmessagesクラスの表示を上書き(append)する。
何を上書きするかと言うと、htmlと言う変数です。
変数に何が入っているかは見えていませんが、どのように表示したいかと言うhtml形式の記述になります。
このような冗長なコードがワンライナーで書けるとQiitaに乗っていたので、驚きでした。
参考URL([Rails]Ajaxを用いて非同期でコメント機能の実装)
上書きするファイル作成
views / comments / _index.html.erb
<% comments.each do |c| %>
<div>
<a href="/users/<%= c.user.id %>"><%= c.user.username %></a>
<%= c.content %>
<% if c.user.id == current_user.id %>
<br>
投稿日:<%= c.created_at.to_s(:datetime_jp) %>
<%= link_to post_comment_path(c.post_id, c.id), method: :delete, remote: true, data: {confirm: "削除しますか?"} do %>
<span class="panel-icon">
<i class="fas fa-trash"></i>
</span>
<% end %>
<hr>
<% else %>
<br>
投稿日:<%= c.created_at.to_s(:datetime_jp) %>
<hr>
<% end %>
</div>
<% end %>
このファイルで先ほどJS変換した情報を使って表示を差し替えています。
つまづいたポイント
データの受け渡しはできているのに、非同期処理されない
自分のイメージはこんな感じです。
投稿フォームから送信された内容をJSファイルに渡し、非同期表示したいファイルに上書きすることにより、その部分だけが切り替わる非同期通信が完了する。

- binding.pry
デバックする時に、createアクションにどのようなparams(情報)が渡ってきているか調べるgemになります。
例えば、createアクションにbinding.pryと記載して、コメントを投稿すると、以下のようになります。
paramsと入力することで、どのようなデータが送信されているか調べることができます。
欲しい値が入ってきているにもかかわらず、表示が変わらなかったので、上書きするViewファイルが怪しいと思い確認。
ポイント
_index.html.erbファイル
<% comments.each do |c| %>
c に commentsを代入しているのに、表示する変数が『comment』だった・・・。
Qiita記事をコピペしていたために起こった不具合でした。
非同期処理はしっかりと理解しないままテックキャンプ を卒業してしまいました。
個人アプリを作る上で非同期はかなり便利!ユーザビリティ向上にも繋がるので、積極的に使っていきます。
ここまで読んでいただき、ありがとうございました。
まだまだ作成中のイクメンアプリですが、コードはこちらから確認できます。(イクメンアプリ)