Ajax(非同期通信)についてわかりやすさ重視でまとめてみた(Rails使用のデモ付)


Webアプリケーション開発を学ぶ中で、多くの人がハマる(と思われる)ポイントであるAjaxについて、 未経験の身で学んだ視点から、わかりやすさ重視でまとめてみました。

今回はjQueryRailsjbuilderを用いたAjaxのコードで解説していきます。

Ajaxとは(厳密な説明)

まずはAjaxの言葉の意味から入っていきます。

AjaxはもともとAsynchronous JavaScript + XMLの略で、Webブラウザ上で動作するJavaScriptでサーバからXMLデータを取得し、取得したデータをコンテンツに動的に反映するという手法です。

(※ データ形式についてですが、冗長なXMLと比べて通信時のデータ量を削減できるなどのメリットから一般的にJSONが利用されることが多いようです。)

このような文章で書かれているページが多いですが、正直、初めてajaxに触れる人にはわかりづらいですよね。笑

Ajaxとは(わかりやすさ重視の説明)

わかりやすくするために具体的な言葉を使いつつ、パーツに分けた上で説明したものがこちらです。

① サーバにHTML形式ではないリクエストを送信し、

② HTMLのファイルを読み込まず(ページ遷移を行わず)に、

③ サーバから取得したデータとJavascriptを用いることで、ページの一部分だけを更新すること。

この記事では、これらを、実際に動くアプリのコードを見ながら、部分ごとに解説していきます。

アプリは、フォームに入力した検索ワードを含む食事の名前をDBから呼び出し、 そのリストを非同期で表示するという、どシンプルなアプリです。 ajax_sample.gif

Ajaxのメリット

詳細説明に入る前に、Ajaxってどこが便利なの?ということを説明したいと思います。

1. ページを遷移することなく、ページの一部だけを更新できる。

ページ遷移ってサイトによってはすごく時間がかかりますよね。 それはもちろんあのhtmlファイルの長いコードを読み込み、それらに含まれるデータの読み込みも行われるからです。

一方、Ajaxを使うと、全体ではなく、一部だけを変えることができるんです。だからスピーディなんですよね。 さらに、ページ遷移の間、画面が真っ白になって操作できない、ってこともあると思うんですけど、 Ajaxを使えば、そのページ遷移がないので、並行して他の操作ができるんです!!

2. 同時にサーバと通信を行うことができる

けど、それってJavaScriptと同じじゃん、って思う人いると思うんですけど、実は決定的な違いがあります。 それは、更新の前にサーバと通信を行うという点です。

それにより何ができるのか。

ページ遷移の画面真っ白を待つことなく、createアクション、destroyアクション、updateアクションなどなどの操作ができるんです! もっと具体的に言うと、ページ遷移をすることなく、コメントができたり、いいねができたり、ユーザ編集ができたりするんです!

①サーバにHTML形式以外のリクエストを送信する

まずはajaxのコードの大枠を説明します。 このjQueryのコードをみてください。

$.ajaxで始まるブロックがajaxについての記述部分です。

大きく分けると、下のように3つに分かれた構造になっています(通信の成功・失敗に関わらず同じ処理をする.always()もありますが、この場では割愛します)。

$.ajax({
  //サーバに送信するリクエストの設定
)}
.done(function(){
  //通信に成功した場合の処理
})
.fail(function(){
  //通信に失敗した場合の処理
})

さらにサーバで送信するリクエストの設定について、もう少し詳しくみてみましょう。

リクエストの設定は、キーと値にて指定します。 以下に挙げているものは基本的なものです。

キー
url Ajaxリクエストを送信するURLを指定します。
type リクエストのタイプ("POST"または"GET")を指定します。
dataType サーバーから返ってくるデータの型を指定します(json, xml, htmlなど)。
data サーバーへ送信するデータです。
キー/値のペアで設定します。

もっと他のオプションも知りたい、という方は以下のページをご参照ください。

$.ajax() | jQuery 1.9 日本語リファレンス | js STUDIO

今回のアプリでは以下のように記載しています。

.search_box
  %input#meal_form
  %input#submit_button{type:"submit"}
  %p 検索結果は以下に表示
  %ul.meal_list
$(function(){
  $("#submit_button").click(function(){ //送信ボタンを押すとイベントが発火します
    var input = $("#meal_form").val(); // フォームの値を'input'という名前の変数に代入します
    $.ajax({
      type:'GET', // リクエストのタイプはGETです
      url: '/meals', // URLは"/meals"を指定します
      data: {keyword: input}, // コントローラへフォームの値を送信します
      dataType: 'json' // データの型はjsonで指定します
    })
    .done(function(data){
      // 通信に成功した場合の処理です
      $('.meal_list').empty(); //前回の検索結果が残っている場合はそれを消します
      data.forEach(function(meal){
        $('.meal_list').append(`<li>${meal.name} </li>`);
      }) //データは配列形式でかえってくるので、forEachで繰り返し処理をします
    })
    .fail(function(){
      // 通信に失敗した場合の処理です
      alert('検索に失敗しました') // alertで検索失敗の旨を表示します
    })
  })
})

②HTMLのファイルを読み込まない

続いて話はコントローラに移ります。先ほどの$.ajaxにおけるurlとtypeの設定により、リクエストを送るコントローラとアクションが決まります。 今回でいうとmealsコントローラのindexアクションです。

そしてそのコントローラには、こちらも先ほどdataにて指定されたデータが送信されます。 このデータは、普段のパラメータ同様、params[:キー]で取得することができます。

class MealsController < ApplicationController

  def index
    @meals = Meal.where("name LIKE(?)", "%#{params[:keyword]}%")
  end

そしてあとはいつも通り、データをモデルに要求し、そのデータを指定されたファイルに渡すんですが、ここでいつもと違う挙動をします。

コントローラは「app/views/コントローラ名/アクション名.リクエストの形式.〇〇」というファイルを探しに行きます。

普段であれば、html形式のリクエストを送っているので、index.html.erbを呼び出しますが、 今回のリクエストはhtml形式ではなく、json形式です。

つまり、index.html.〇〇ではなく、index.json.〇〇を呼び出します。

ここが重要な点です。これにより、サーバとデータのやりとりだけを行った後、普段であればページ遷移を行うところですが、ajaxではページ遷移を行わずに、index.json.jbuilderを通った上で、先ほどのjsファイルの.done以下の処理に戻るのです。

jbuilderは、簡単にいうと、データをJSON形式で出力するテンプレートエンジンです。ここでは詳細説明は割愛しますが、JSON形式でデータをjavascriptに渡すとご理解いただければ大丈夫です。

# コントローラで定義した@mealsに対し、繰り返し処理を行い、jsonの配列を作成します。
json.array! @meals do |meal|
  # meal.nameがjsonデータのnameに代入されます
  # json.〇〇の〇〇がデータから値を取り出す時に使う変数名となります
  json.name meal.name
end

JavaScriptを用いることでページの一部分だけを更新する

さて、通信が成功すると、.done()以下の部分の処理に遷移します。ここでは.done(data)と引数を取ってあげることで、コントローラからjbuilderを通って渡されたデータを処理の中で使うことができるようになります。

あとの処理の記載は普段のjqueryと同じように書きましょう。 これによってページ遷移をすることなく、サーバから取り寄せたデータを使って画面を一部更新することができました。

$(function(){
  $("#submit_button").click(function(){ //送信ボタンを押すとイベントが発火します
    var input = $("#meal_form").val(); // フォームの値を'input'という名前の変数に代入します
    $.ajax({
      type:'GET', // リクエストのタイプはGETです
      url: '/meals', // URLは/mealsを指定します。つまりmeals#indexのアクションをリクエストします
      data: {keyword: input}, // コントローラへフォームの値を送信します
      dataType: 'json' // データの型はjsonで指定します
    })
    .done(function(data){
      // 通信に成功した場合の処理です
      $('.meal_list').empty(); //前回の検索結果が残っている場合はそれを消します
      data.forEach(function(meal){
        $('.meal_list').append(`<li>${meal.name} </li>`);
        //データは配列形式でかえってくるので、forEachで繰り返し処理をします
        //ここではデータの単数の変数名をmealと置いていますが、何でも構いません。
        //ただし、値を取り出す場合は"ここで定義した変数名"."json.jbuilderで定義した〇〇(この例ではname)"で取得します。
      }) 
    })
    .fail(function(){
      // 通信に失敗した場合の処理です
      alert('検索に失敗しました') // alertで検索失敗の旨を表示します
    })
  })
})

まとめ

ajaxは、自分自身はもちろん、同じスクールに通っていた受講生の方でも多くの方が苦手とされていた部分でした。

その主な原因は

  • そもそもajaxとは何ができるものなのか、という点が曖昧になっている
  • 構造が少し複雑で、データがどこを通っているのか、どこでエラーが発生しているのかがわかりづらいという点

この2点じゃないかなと思います。 この記事が、そういった方々の助けになれていたら嬉しいです。

またjs.erbとremote:trueを用いてもっと簡単にajaxを実装する方法についても記事をまとめていますので、そちらもご覧いただければ幸いです。