#1.はじめに
現在作成中のWebアプリケーション(育児サイトーSUKUSUKUー)のホーム画面のページネーションについて、別ページに移動する際、いちいちリロードされて、ページ上部に移動されるのがストレスに感じました。なのでページネーションをAjax対応にして、使いやすくしたいなと考えました。
この記事でわかること
・Ajax通信の基本的な仕組み
・代表的なJavaScriptライブラリ
・RailsにおけるAjax
・ページネーションをAjax対応にする
#2.そもそもAjaxとはなにか?
Ajax(エイジャックス、アジャックス)とは、wikipediaによると、
ウェブブラウザ内で非同期通信を行いながらインターフェイスの構築を行うプログラミング手法である。
XMLHttpRequest(HTTP通信を行うためのJavaScript組み込みクラス)による非同期通信を利用し、通信結果に応じてダイナミックHTML (DHTML) で動的にページの一部を書き換えるというアプローチを取る。
AjaxはAsynchronous JavaScript + XML の略で、2005年2月18日に米国のインフォメーションアーキテクトであるJesse James Garrettにより名付けられた。
とのこと。もう少し詳しく説明していきます。
####非同期通信とは?
同期通信あっての非同期通信なので、まずは同期通信について説明します。同期通信では下記の図のようにユーザーインターフェイス上でなんらかの操作し、そのリクエストをWebサーバーへ送ります。そして、Webサーバーはそのレスポンスを受取り、データベース等で処理を行い、その結果をHTML,CSS等のレスポンスでブラウザ側へ送信します。ブラウザはリロードされ、結果が表示されます。
同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザはリロードされていました。
非同期通信では、JavaScriptを使用しウェブブラウザ内で動的な処理が行なえます。下記の図のようにユーザーインターフェイス上で何らかの処理を行った際に、直接サーバーへ通信を行うのではなく、ブラウザ内のAjaxエンジンへJavaScriptの呼び出しを行います。AjaxエンジンはレスポンスとしてHTML,CSSをユーザーインターフェイスへ送り、DOMがブラウザへ表示されます。この時点ではブラウザとサーバー同じ状態ではないため、AjaxエンジンよりWebサーバーへ**XMLHTTPリクエスト(XHR)**を送ります。リクエストを受け取った結果をサーバー側はXML、JSONとしてレスポンスします。
非同期通信の地図アプリでは、現在表示している位置から、移動したり、拡大した場合、ブラウザ側でリロードされずに動的な処理ができます。
つまり、
同期通信:サーバーの通信を待ってからブラウザがリロードされ動的な処理ができる。
非同期通信:サーバーの通信を待たずに、動的な処理ができる。その際ブラウザはリロードされない。
ということです。
####XMLとは?
Extensible Markup Languageの略。マークアップ言語の一つ。HTMLはWebページを表示するための言語に対して、データを交換するためや設定ファイルを書くときに使用する言語です。タグなどを自由に編集できるため、「拡張可能なマークアップ言語」とよばれます。
<?xml version="1.0" encoding="UTF-8" ?>
<食料品>
<食料>
<名前>リンゴ</名前>
<色>赤</色>
</食料>
</食料品>
このようにタグは日本語でも記述可能です。
####JSONとは?
JavaScript Object Notationの略。 XMLと同様の共通データ定義言語です。人間にとって読み書きが容易で、マシンにとっても簡単にパースや生成を行なえる形式です。
参考にしました→JSONの紹介
####DOMとは?
Document Object Modelの略。HTMLやXML文書のためのプログラミングインターフェイスです。DOMはHTMLやXMLをツリー構造として展開します。AjaxではDOMを使用してユーザーインターフェイスの一部分だけ書き換えます。
参考にしました→DOMの紹介
#3.Ajaxを使用するメリット、デメリット
####メリット
・サーバーからのレスポンスを待たずに、高速に動的な処理ができる。
・受信するデータ量が減らせる。
・サーバーに負担がかからない。
####デメリット
・開発の際、ブラウザがリロードされないため、エラーメッセージが画面に表示されない。エラーがどこに出てるか分かりづらい。
・クロスブラウザ対策[^1]が必要
・バリデーションをJS,サーバーサイド両方で記載しないといけない。
主に、メリットはユーザー側にあり、デメリットは開発側にありそうですね。
#4.JavaScriptのフレームワーク
Ajaxを使用するためにはJavaScriptを学ぶ必要があります。JavaScriptを書きやすくするフレームワークがいくつかありますので、ここでは代表的なものを紹介します。
####jQuery
Ajax処理を簡単にするために最もよく利用されてきたフレームワーク。
文法も簡単でシンプルなので覚えやすい。ブラウザの種類に依存せず、アニメーションから効果、非同期通信に関わる部分まで幅広く対応することができます。
####Angular
Googleと個人や企業のコミュニティによって開発されているフレームワーク。主に大規模なアプリケーションに向いている。
独自の概念や機能が多く有るので、学習コストは高め。
####React
Facebookとコミュニティによって開発されているフレームワーク。
UI部分のみのライブラリなので、他のフレームワークと併用してしようする。
その代わり動作は早い。
####Vue.js
シンプル・軽量・高速。強力なコンポーネント機能により、データや処理を分けることができ、サーバーとの通信も高速で行うことができる。
#5.RailsにおけるAjax
Railsでは、JavaScriptをDOMに追加する際の手法を**「UJS: Unobtrusive(控えめな)JavaScript」**と呼んでるそうです。
これはHTML上にJavaScriptを混入させると、DRY[^2]の原則に反することが多くなる上に、煩雑になり保守がしづらくなります。そのためJavaScriptを正しく分離し表に出さない手法をRailsではとっています。Railsでは、こうした「最小化」と「連結」によって、あらゆるJavaScriptを実行できます。
実際にリンクにJavaScriptを組み込むには、ビュー上で下記のようにremoteオプションをオンにします。
<%= link_to "記事", @article, remote: true %>
これでJavaScriptを組み込む事ができます。
参考→Rails で JavaScript を使用する
#6.ページネーションをAjax対応にする
####環境
-
MacBook Catalina ver.10.15.7
-
Rails 6.0.3
-
ruby 2.7.1
-
jQuery
今回は学習コストを考慮しフレームワークはjQueryを使用することとしました。
####RailsにjQueryを入れる
まず、WebpackとYarnこの両方にjQueryを追加する必要があります。
$ yarn add jquery
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します。
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("jquery")
これで、RailsでjQueryを使用できます。
####ビュー(HTML)
最初にビューをAjax対応できるようにします。
ー ページネーション部分だけ記載 ー
.
.
<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が下記です。
<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">次 ›</a>
</li>
<li>
<a data-remote="true" href="/?page=2">最後 »</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)
もっと簡単なやり方がありそうですが、とりあえずうまく動きました!!
#7.まとめ
Rubyの勉強はそこそこやってましたが、JavaScriptはよく知らなかったのでまとめました。そこそこ学習コストは高い上に、初学者にとって保守が難しそうなので、必要な部分を見極めたいです。
[^1]: すべてのブラウザで動作する保証。
[^2]: Don't Repeat Yourself「繰り返しを避けること」