【Rails×Ajax】いいね機能ハンズオン

# Ajaxで「いいね機能」を実装するハンズオン

<font color="IndianRed">１年以上内容を更新しておりませんので、最新の実装ではありません。ご了承ください。</font>

### はじめまして
こんにちは。
普段は餃子のことを考えながら ~~DMM WEBCAMPの裏で~~ 開発をしています。

DMM WEBCAMPはプログラミングスクールを運営しているのですが、よく生徒さんから
**「Ajaxを取り入れたアプリケーションを作成したい」**
**「Ajaxが結局よくわからない」**
**「難しそう」**
との声を頂くことが多く、折角なのでRailsのAjax処理の全体を見通しよく、そしてサクッと実装できるような記事を書き綴ってみました。ハンズオンになっているので、是非少しでも興味のある方は一緒にコーディングしながらAjaxの流れを掴んでいきましょう！

### そもそもAjaxって...?
Ajax(エージャックス/エイジャックス)とは
「Asynchronous JavaScript + XML」の略です。
ちょっとさっぱりわからないですよね。
こちらのリンク先に丁寧な解説があるので読んでみてください。
[初心者目線でAjaxの説明](https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9)

Ajaxは部分的にデータを更新したり、画面の切り替えを行っています。
ちなみに、普段の生活の中でこんな場面よくありませんか。

<img width="872" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/69b17734-d7e8-a34f-103b-4b77906af3ec.png">



最近のアプリケーションではAjaxの処理が多くの場面で取り入れられています。
TwitterやFacebook,QiitaのいいねもAjaxで処理されていますね。

では実際にRailsでは「いいね」を押した後(オレンジ色の矢印の部分)に、Rails側でどのような処理がされているのかイメージしながらコーディングしていきましょう。



## RailsでAjax処理のいいね機能を実装しよう
1. 実装の全体像を確認しよう
2. 「いいね」機能のついたアプリケーションを準備
3. 「いいね」をクリックした時に、Ajaxの処理をさせよう
4. 部分テンプレートを作成しよう
5. 部分的に更新させよう

### 1. 実装の全体像を確認しよう
まず先に実装の全体像を掴んでおきましょう。
先程の画像にあるオレンジ色の矢印は、このような処理をしています。

<img width="921" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/f77a5aa7-5f91-2b96-b525-46aeea6a490d.png">


なんとなくイメージだけ掴んでおいてください。
難しくないので、要所要所追って進めていきましょう。

### 2. 「いいね」機能のついたアプリケーションを準備
まずは準備段階として、

- ユーザー(User)
- 投稿(Blog)
- いいね(Like)

を実装した想定で進めます。

もし「いいね機能」「アソシエーション」が不安な方は、是非この記事を読んでみてください。丁寧すぎるくらい丁寧です。
[【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】🎸](https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1)

また簡素な「いいね機能」のついたアプリケーションをgithubに置いておくので、Ajaxを試してみたい方はご自由にご利用ください。
[github:ajax_like_app](https://github.com/naberina/ajax_like_app)


「いいね」機能が実装されているアプリケーションが作成できました。
<img width="720" alt="AjaxApp.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/87539dd4-c0b8-95f0-21aa-234792b0bcef.png">

ちなみに現在の「いいね」機能はAjaxではないので、「いいね」をクリックする度に
コントローラでリダイレクトされてページが更新されます。

<img width="922" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/f27389ae-dce6-7d70-0c79-35037f0d15c1.png">


左上のロードアイコンが更新されているように、ページが更新されています。
![Large GIF (520x488).gif](https://qiita-image-store.s3.amazonaws.com/0/168550/401109e9-7cd6-e5a4-345d-3e492b6a402b.gif)


ここから、Ajax(非同期)で「いいね」を実装していきましょう。
たったの**3ステップ**で実装できます。


### 3. 「いいね」をクリックした時に、Ajaxの処理をさせよう

<img width="917" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/f8e9a984-eab4-8d46-d092-4d1094c608e4.png">



それがめちゃめちゃ簡単です。
いいねを送信しているリンクに`remote: true` を追加しましょう。
こうすることで、「いいね」をクリックすると**js形式のリクエストを送信する**ことができます。

```erb:index.html.erb
<% if blog.liked_by?(current_user) %>
     <p><span>お気に入り解除: </span><%=link_to blog.likes.count, blog_likes_path(blog.id), method: :delete, remote: true %></p>
<% else %>
     <p><span>お気に入り登録: </span><%=link_to blog.likes.count, blog_likes_path(blog.id), method: :post, remote: true %></p>
<% end %>
```

ページが更新される「いいね」の処理は、以下の画像のように
LikesControllerにHTMLリクエストが送信され、データベースにデータを保存・削除し、画面にリダイレクトする処理が行われています。
<img width="1440" alt="1___CMD__100______LINE_____fsevent_watch_.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/ddc658e9-7571-493b-b592-6136f30d3c43.png">

リンクに`remote: true` を指定すると
LikesControllerにjavascriptのリクエストが送信されます。
このままでは、まだ機能は完成していませんが、JSのリクエストを簡単に送ることが確認できました。

<img width="1050" alt="1___CMD__100______LINE_____fsevent_watch_.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/0438934e-16c2-7852-739e-f6612aa95e53.png">


### 4. 部分テンプレートを作成しよう

図にすると次は②、③を行うのですが、すでにコントローラで`Like.save`の処理をしているので、他に何か手を加える必要はありません。
<img width="917" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/0f008716-4092-59f2-6b30-d67d59810775.png">


そのため、④更新したい部分のビューを作成しましょう。

<img width="917" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/1e610b15-07ac-fad7-3839-51d9528bfdc1.png">



`views/likes/_like.html.erb` 部分テンプレートを作成します。
具体的には、部分的に更新したい「いいね」の箱を用意して、
「いいね」しているか、していないかという状態で表示を切り替えます。

このようなイメージです。

<img width="911" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/e907f7f2-2147-ad36-e662-cbfcf25ad7ec.png">


まずは更新するボックスを作成しましょう。
中身の`render`で「いいね」を切り替える処理を記述します。

```erb:views/blogs/index.html.erb
<div class="main_contents">
	<% @blogs.each do |blog| %>
		<div class="blog_contents">
		  <h2><%= link_to blog.title, blog_path(blog.id) %></h2>
		  <%= blog.body %>

         <!-- ↓追加(部分的に更新したい場所)↓ -->
		  <div id="likes_buttons_<%= blog.id %>">
			 <%= render partial: 'likes/like', locals: { blog: blog} %>
		  </div>
         <!-- ↑ここまで↑ -->
		</div>
  <% end %>
</div>
```

index.html.erbで呼び出される`render`の中身をみていきましょう。
ログイン中のユーザーがいいねしているかしていないかを判断して表示を切り替えています。

```erb:views/likes/_like.html.erb
<% if user_signed_in? %>
    <% if blog.liked_by?(current_user) %>
        <p><span>お気に入り解除: </span><%=link_to blog.likes.count, blog_likes_path(blog.id), method: :delete, remote: true %></p>
    <% else %>
        <p><span>お気に入り登録: </span><%=link_to blog.likes.count, blog_likes_path(blog.id), method: :post, remote: true %></p>
    <% end %>
<% else %>
    <p><span>お気に入り数: </span><%= blog.likes.count %></p>
<% end %>

```

`remote: true` で送信された値を
LikesControllerで受け取り、「いいね」を登録、または取り消します。
createアクションやdestroyアクションではビューを作成していないので、普通はリダイレクト処理でページに遷移させますが、
`remote: true`でjavascript形式のリクエストを送信しているため、`create.js.erb`/`destroy.js.erb`ファイルを作成し、部分的な更新をします。



```js:views/likes/create.js.erb
// #likes_buttons_<%= @blog.id %>この部分のHTMLだけ、renderで部分的に更新するという処理です
$('#likes_buttons_<%= @blog.id %>').html("<%= j(render partial: 'likes/like', locals: {blog: @blog}) %>");
```

```js:views/likes/destroy.js.erb
$('#likes_buttons_<%= @blog.id %>').html("<%= j(render partial: 'likes/like', locals: {blog: @blog}) %>");
```

### 5. 部分的に更新させよう

すでに90%完成しているのですが、まだLikeControllerでリダイレクト処理が残ってしまっているので、
削除しましょう。

<img width="923" alt="無題のプレゼンテーション_-_Google_スライド.png" src="https://qiita-image-store.s3.amazonaws.com/0/168550/f505035f-ed91-6fbe-e0dc-a1abcf4e45cb.png">


```ruby:LikesController
class LikesController < ApplicationController

  def create
    @blog = Blog.find(params[:blog_id])
    like = current_user.likes.build(blog_id: params[:blog_id])
    like.save
    redirect_to blogs_path #←この行を削除
  end

  def destroy
    @blog = Blog.find(params[:blog_id])
    like = Like.find_by(blog_id: params[:blog_id], user_id: current_user.id)
    like.destroy
    redirect_to blogs_path #←この行を削除
  end
end
```

### 大事なポイント
railsでjQueryを読み込めるようにgemをインストールしましょう。
これをインストールしないと、ページを更新しないといけなない仕様になってしまいます。

```ruby:Gemfile
gem 'jquery-rails'
```

gemを追加したのでgemを読み込みましょう。

```:ターミナル・コマンドプロンプト
bundle install
```

また、application.jsにもjqueryをrequireします。

```erb:application.js
//
//= require jquery ←追加
//= require rails-ujs
```


サーバーを立ち上げて、実装できているか確認してみてください。
このようにページの更新なしで、「いいね」機能が実装できました。
![ajax.gif](https://qiita-image-store.s3.amazonaws.com/0/168550/1d4f247f-bbb0-9c4a-6c65-4b9b84dc9c5c.gif)

### 最後に
RailsのAjaxは意外と簡単です。~~そんな気がしてきませんか。~~

```erb:3つのポイント
1. jsのリクエストを送信する(remote: true)
2. 部分テンプレートを作成する
3. リロードしない処理を設定(コントローラのredirect削除・jqueryインストール)
```

この記事を通してRailsのAjax処理の流れが簡単にイメージできるようになれば嬉しいです。
Ajaxを使えば「投稿」「表示」「更新」「削除」などのCRUDの処理を、リロードせずに実装することができます。是非興味のある方は挑戦してみてください。実装お疲れ様でした。


また、この記事に対して間違えている部分や
オススメの餃子屋さんがあれば、コメントして頂けると助かります。