【Rails】ページネーションをAjax対応にする

#１．はじめに
　現在作成中のWebアプリケーション（育児サイトー*SUKUSUKU*ー）のホーム画面のページネーションについて、別ページに移動する際、いちいちリロードされて、ページ上部に移動されるのがストレスに感じました。なのでページネーションをAjax対応にして、使いやすくしたいなと考えました。
![ezgif-7-71eb2ec20490.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/6157e585-10b8-cda2-2b00-bb0649887b36.gif)

　　**この記事でわかること**
　　　　・Ajax通信の基本的な仕組み
　　　　・代表的なJavaScriptライブラリ
　　　　・RailsにおけるAjax
　　　　・ページネーションをAjax対応にする

#２．そもそもAjaxとはなにか？
　Ajax(エイジャックス、アジャックス)とは、[wikipedia](https://ja.wikipedia.org/wiki/Ajax)によると、

>ウェブブラウザ内で非同期通信を行いながらインターフェイスの構築を行うプログラミング手法である。
>XMLHttpRequest（HTTP通信を行うためのJavaScript組み込みクラス）による非同期通信を利用し、通信結果に応じてダイナミックHTML (DHTML) で動的にページの一部を書き換えるというアプローチを取る。
>AjaxはAsynchronous JavaScript + XML の略で、2005年2月18日に米国のインフォメーションアーキテクトであるJesse James Garrettにより名付けられた。

とのこと。もう少し詳しく説明していきます。

####非同期通信とは？

　同期通信あっての非同期通信なので、まずは同期通信について説明します。同期通信では下記の図のようにユーザーインターフェイス上でなんらかの操作し、そのリクエストをWebサーバーへ送ります。そして、Webサーバーはそのレスポンスを受取り、データベース等で処理を行い、その結果をHTML,CSS等のレスポンスでブラウザ側へ送信します。ブラウザはリロードされ、結果が表示されます。

![名称未設定ファイル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/c74967ff-1197-4ed9-1dd8-c61ae46b41bf.png)


　同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザはリロードされていました。

![r20imasara03_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/93b6206e-f243-3720-0381-6f7ef3173bfc.gif)

　非同期通信では、JavaScriptを使用しウェブブラウザ内で動的な処理が行なえます。下記の図のようにユーザーインターフェイス上で何らかの処理を行った際に、直接サーバーへ通信を行うのではなく、ブラウザ内のAjaxエンジンへJavaScriptの呼び出しを行います。AjaxエンジンはレスポンスとしてHTML,CSSをユーザーインターフェイスへ送り、DOMがブラウザへ表示されます。この時点ではブラウザとサーバー同じ状態ではないため、AjaxエンジンよりWebサーバーへ**XMLHTTPリクエスト(XHR)**を送ります。リクエストを受け取った結果をサーバー側はXML、JSONとしてレスポンスします。

![非同期通信.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/4c2b0024-9cc7-aae1-3c9c-840657746ffa.png)


　非同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザ側でリロードされずに動的な処理ができます。

![ezgif-4-050dab99fd38.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/a5bef25a-c0ad-1346-b6d6-eab9faaa7ee5.gif)

つまり、

**同期通信**：サーバーの通信を待ってからブラウザがリロードされ動的な処理ができる。

**非同期通信**：サーバーの通信を待たずに、動的な処理ができる。その際ブラウザはリロードされない。

ということです。

####XMLとは？
　Extensible Markup Languageの略。マークアップ言語の一つ。HTMLはWebページを表示するための言語に対して、データを交換するためや設定ファイルを書くときに使用する言語です。タグなどを自由に編集できるため、「拡張可能なマークアップ言語」とよばれます。

```XML
<?xml version="1.0" encoding="UTF-8" ?>
<食料品>
  <食料>
    <名前>リンゴ</名前>
    <色>赤</色>
  </食料>
</食料品>
```
このようにタグは日本語でも記述可能です。

####JSONとは？
　JavaScript Object Notationの略。 XMLと同様の共通データ定義言語です。人間にとって読み書きが容易で、マシンにとっても簡単にパースや生成を行なえる形式です。

参考にしました→[JSONの紹介](https://www.json.org/json-ja.html)

####DOMとは？
　Document Object Modelの略。HTMLやXML文書のためのプログラミングインターフェイスです。DOMはHTMLやXMLをツリー構造として展開します。AjaxではDOMを使用してユーザーインターフェイスの一部分だけ書き換えます。

参考にしました→[DOMの紹介](https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction)


#３．Ajaxを使用するメリット、デメリット
####メリット
　・サーバーからのレスポンスを待たずに、高速に動的な処理ができる。

　・受信するデータ量が減らせる。

　・サーバーに負担がかからない。

####デメリット
　・開発の際、ブラウザがリロードされないため、エラーメッセージが画面に表示されない。エラーがどこに出てるか分かりづらい。

　・クロスブラウザ対策[^1]が必要

　・バリデーションをJS,サーバーサイド両方で記載しないといけない。

主に、メリットはユーザー側にあり、デメリットは開発側にありそうですね。

#４．JavaScriptのフレームワーク
　Ajaxを使用するためにはJavaScriptを学ぶ必要があります。JavaScriptを書きやすくするフレームワークがいくつかありますので、ここでは代表的なものを紹介します。

####[jQuery](https://jquery.com/)
　Ajax処理を簡単にするために最もよく利用されてきたフレームワーク。
　文法も簡単でシンプルなので覚えやすい。ブラウザの種類に依存せず、アニメーションから効果、非同期通信に関わる部分まで幅広く対応することができます。

####[Angular](https://angular.jp/)
　Googleと個人や企業のコミュニティによって開発されているフレームワーク。主に大規模なアプリケーションに向いている。
　独自の概念や機能が多く有るので、学習コストは高め。

####[React](https://reactjs.org/)
　Facebookとコミュニティによって開発されているフレームワーク。
　UI部分のみのライブラリなので、他のフレームワークと併用してしようする。
　その代わり動作は早い。

####[Vue.js](https://v3.vuejs.org/)
　シンプル・軽量・高速。強力なコンポーネント機能により、データや処理を分けることができ、サーバーとの通信も高速で行うことができる。

#５．RailsにおけるAjax
　Railsでは、JavaScriptをDOMに追加する際の手法を**「UJS: Unobtrusive（控えめな）JavaScript」**と呼んでるそうです。
　これはHTML上にJavaScriptを混入させると、DRY[^2]の原則に反することが多くなる上に、煩雑になり保守がしづらくなります。そのためJavaScriptを正しく分離し表に出さない手法をRailsではとっています。Railsでは、こうした「最小化」と「連結」によって、あらゆるJavaScriptを実行できます。
　実際にリンクにJavaScriptを組み込むには、ビュー上で下記のようにremoteオプションをオンにします。

```HTML
<%= link_to "記事", @article, remote: true %>
```

これでJavaScriptを組み込む事ができます。
　
参考→[Rails で JavaScript を使用する](https://railsguides.jp/working_with_javascript_in_rails.html)

#６．ページネーションをAjax対応にする

####環境

- MacBook　Catalina ver.10.15.7

- Rails 6.0.3

- ruby 2.7.1

- [gem "kaminari"](https://github.com/kaminari/kaminari)

- jQuery

今回は学習コストを考慮しフレームワークは**jQuery**を使用することとしました。

####RailsにjQueryを入れる
　まず、WebpackとYarnこの両方にjQueryを追加する必要があります。

```terminal:ターミナル
$ yarn add jquery
```

```javascript:config/webpack/environment.js
const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.prepend('Provide',
  new webpack.ProvidePlugin({
    $: 'jquery/src/jquery',
    jQuery: 'jquery/src/jquery'
  })
)

module.exports = environment
```

application.jsファイルでjQueryをrequireします。

```javascript:app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")
```

これで、RailsでjQueryを使用できます。


####ビュー（HTML）

　最初にビューをAjax対応できるようにします。

```HTML:app/views/static_pages/home.html.erb
ー　ページネーション部分だけ記載　ー
  .
  .
<div class="question-index">
  <%= paginate @questions, remote: true %>
    <%= render partial: 'questions/question', collection: @questions %>
  <%= paginate @questions, remote: true %>
</div>
  .
  .
```
``pagenate``に``remote: true``を記載します。
このhome.html.erbで生成されるページネーションのHTMLが下記です。

````HTML:HTML
<ul class="pagination">
  <li class='active'>
    <a data-remote="true">1</a>
  </li>
  <li>
    <a rel="next" data-remote="true" href="/?page=2">2</a>
  </li>
  <li>
    <a rel="next" data-remote="true" href="/?page=2">次 &rsaquo;</a>
  </li>
  <li>
    <a data-remote="true" href="/?page=2">最後 &raquo;</a>
  </li>
</ul>
```

すべてのページネーションのリンクに``data-remote="true"``が付きました。
この事により、JavaScriptによるフォーム操作を許可することをRailsに知らせることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/0cb6e3b0-2b9f-c5cc-3fd1-ef28f16e879d.png" width="30%">
↑生成されたページネーション。

####コントローラー

次にコントローラーでAjaxリクエストに応答できるようにします。

```ruby:app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
    @questions = Question.page(params[:page]).per(10).all
    respond_to do |format|
      format.html
      format.js
    end
  end

end
```

``respond_to``メソッドはブロック内のコードの内いずれか一つがが実行されます。
普段はレスポンスとして``format.html``が呼び出されますが、今回ページネーションのリンクは``data-remote="true"``として``remote``オプションがオンになっているため、``format.js``がよびだされます。

####ビュー（HTML）
最後にjQueryを使用して、Ajax対応させます。
その前にjQueryで呼び出しできるようhome.html.erbに記載されているコードをパーシャルに移します。

```HTML:app/views/static_pages/home.html.erb（見直し）
<div id="home_pagenate">
  <%= render partial: 'index_page' %>
</div>
```

元あったhome.html.erbのコードをパーシャルへ貼り付けます。

```HTML:app/views/static_pages/_index_page.html.erb
ー　ページネーション部分だけ記載　ー
  .
  .
<div class="question-index">
  <%= paginate @questions, remote: true %>
    <%= render partial: 'questions/question', collection: @questions %>
  <%= paginate @questions, remote: true %>
</div>
  .
  .
```

JSファイルを作成し、jQueryのコードを記載します。

```javascript:home.js.erb
 $('#home_pagenate').html("<%= escape_javascript(render 'index_page') %>");
```
これで、``data-remote="true"``の記載があるページネーションが実行された際に、コントローラーでJSファイルが呼び出され、JSファイルで元あったページがレンダリングされます。これでAjax対応は完成です。

####完成形

![ezgif-6-bfbd4116adbc.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687088/7e84ee1e-7dfc-e6bf-3665-e6a201675750.gif)

もっと簡単なやり方がありそうですが、とりあえずうまく動きました！！

#７．まとめ
　Rubyの勉強はそこそこやってましたが、JavaScriptはよく知らなかったのでまとめました。そこそこ学習コストは高い上に、初学者にとって保守が難しそうなので、必要な部分を見極めたいです。

[^1]: すべてのブラウザで動作する保証。
[^2]: Don't Repeat Yourself「繰り返しを避けること」