Ajax によるデータの更新
2015/06/14
前回は、イベントハンドラーについて解説しました。ユーザーが HTML 要素(例えば、チェックボックス)に何らかのアクション(例えば、クリック)をしたときに実行される関数がイベントハンドラーです。
今回は、イベントハンドラーの中で Ajax コールを行いデータベースを更新する方法について説明します。
本題に入る前に、イベントハンドラーの働きを視覚的に分かりやすくするため、スタイルシートを適用します。
テキストエディタで app/assets/stylesheets
ディレクトリに、新規ファイル todo_list.scss
を次のような内容で作成してください。
#todo-list {
label.completed span {
color: #888;
text-decoration: line-through;
}
}
ファイル名の拡張子 .scss
はこのファイルが Sass/SCSS 形式で書かれていることを意味します。SCSS は CSS を拡張したスタイルシート言語で、Rails によって自動的に CSS に変換されます。SCSS の第一の特長は、セレクタを入れ子にできることです。上の例では、todo-list
という ID を持つ要素の内側にある completed
クラスの label
要素のさらに内側にある span
要素のスタイルを設定しています。
そして、TodoList
コンポーネントの render()
メソッドを修正します。テキストエディタで app/assets/javascripts/todo_list.es6
の該当部分を次のように書き換えてください(m.label
の前に1行挿入)。
render(m) {
m.ul(m => {
this.tasks.forEach(task => {
m.li(m => {
m.class({ completed: task.done });
m.label(m => {
m.onclick(e => this.toggleTask(task));
m.input({ type: 'checkbox', checked: task.done }).sp();
m.span(task.title);
});
});
});
});
}
挿入されたのは次の1行です。
m.class({ completed: task.done });
この結果、done
属性が「真」であるタスクの場合、label
要素に completed
クラスが設定されます。Rails アプリケーションを起動し、ブラウザで http://localhost:3000/
を開くと次のような画面になります。
ここで1番目のチェックボックスをクリックすると「1」という警告ダイアローグが表示されますので、「OK」ボタンをクリックしてください。「猫のえさを買う。」という文字列のスタイルが変わらない(色がグレーにならず、取り消し線も表示されない)点を確認してください。
次に、TodoList
コンポーネントの toggleTask()
メソッドを修正します。テキストエディタで app/assets/javascripts/todo_list.es6
の該当部分を次のように書き換えてください。
toggleTask(task) {
task.done = !task.done;
this.refresh();
}
今回の最終的な目標は Ajax でデータベースを更新することですが、この段階ではまだそこまで作り込んでいません。まずは、TodoList
コンポーネントが所有しているデータを更新し、コンポーネントのレンダリングをやり直しているだけです。
ブラウザをリロードしてから、1番目のチェックボックスをクリックしてください。先ほどと異なり、チェックを入れたり外したりするのに応じて、「猫のえさを買う。」という文字列のスタイルが変化する点に注目してください。toggleTask
メソッドの引数 task
は、このコンポーネントが所有しているタスクリストに含まれる特定のタスクを参照しています。その done
属性の値を反転させてから this.refresh()
を呼べば、コンポーネント全体が再描画されてタスクの題名のスタイルが変化するのです。
jQuery のやり方に慣れているが「仮想 DOM」に慣れていない読者の方は、この実装方法に戸惑うかもしれません。jQuery でこの種の処理を行う場合、$(e.target).parent().toggleClass("completed");
のようなコードで直接 HTML 要素のスタイルを変更します。しかし、Cape.JS ではコンポーネント全体が再描画されるのです。こう書くと、パフォーマンスが心配になるかもしれませんが、その心配を克服するための仕組みが「仮想 DOM」です。Cape.JS のベースになっている virtual-dom
ライブラリは、仮想 DOM と本物の DOM の間の差分を計算して、変更が必要な部分だけを描き直します。
続いて、Rails 側の API を実装します。まず、config/routes.rb
を次のように書き換えてください。
Rails.application.routes.draw do
root 'top#index'
namespace :api do
resources :tasks, only: [ :index, :update ]
end
end
下から3行目が変更箇所です(コンマと :update
を挿入)。
そして、app/controllers/api/tasks_controller.rb
を次のように書き換えます。
class Api::TasksController < ApplicationController
def index
@tasks = Task.order(id: :asc)
end
def update
task = Task.find(params[:id])
if task.update_attributes(task_params)
render text: 'OK'
else
render text: 'NG'
end
end
private
def task_params
params.require(:task).permit(:title, :done)
end
end
この変更の意味について説明を始めてしまうと非常に長くなってしまいます。Rails 開発未経験の方は、いったんこのままソースコードを受け入れてください。
params
、find
、update_attributes
、render
などのメソッドに関しては初級の教科書やチュートリアルで学べます。task_params
メソッドの役割については「Strong Parameters」というキーワードでネット検索して調べてください。
最後に、この API を呼び出すコードを TodoList
コンポーネントの toggleTask()
メソッドに組み入れます。
toggleTask(task) {
$.ajax({
type: 'PATCH',
url: `/api/tasks/${task.id}`,
data: { task: { done: !task.done } }
}).done(data => {
if (data === 'OK') {
task.done = !task.done;
this.refresh();
}
});
}
Rails で「データの更新」を意味する PATCH
メソッドで API にアクセスしています。仮に task.id
の値が 1 で task.done
が「偽」の場合、/api/tasks/1
という URL パスに対して { task: done: true }
というデータが送信されることになります。
`/api/tasks/${task.id}`
という書き方に注意してください。ECMAScript6 ではバッククオートで囲まれた文字列では、式の埋め込み(interpolation)が行われます。すなわち、${task.id}
の部分には task.id
の値(例えば「1」という整数)が文字列に変換されて挿入されます。
Rails アプリケーションはこれを受けてデータベースを更新し、「OK」という文字列を返します。Cape.JS 側ではそれを受けて done
メソッドのパラメータ data
にセットしますので、if 文の条件式が成立して当該タスクの done
属性が反転されます。そして、this.refresh()
が呼ばれてコンポーネント全体が再描画される、という流れです。
では、動作確認をしましょう。ブラウザをリロードしてから、1番目のチェックボックスにチェックを入れます。すると、次のような画面に変わります。
ここでもう一度ブラウザをリロードしてみてください。1番目のチェックボックスにチェックが入ったままになっていれば、データベースが更新されたことになります。
今回はここまでとします。説明を省略したところもあり、少し難しく感じられたかもしれません。ごめんなさい。
次回は、データストアについて解説する予定です。