はじめに
RailsでAjaxを実装するとなり、ほぼ初めてJavaScriptに触りました。
そんな私が理解に苦しんだところやハマったポイントをまとめました。
実装するもの
Ajax関数による非同期通信でコメントの編集をするというものです。
以下のような流れになります。
編集ボタンを押す
→ 編集フォームが現れる
編集中、キャンセルボタンを押す
→ 変更せずにコメントを表示
編集後、更新ボタンを押す
→ データを更新して、編集後のコメントを表示する
→ 不適な場合(バリデーションエラー)、エラーメッセージを表示し、編集フォームはそのままにする
そもそもAjaxの処理の流れってどうなってんの
基本的に、イベントの中にAjax関数を入れることになります。
Ajax関数の役割ですが、
HTTP通信でページを読み込みます。
jQuery日本語リファレンス
Ajaxリクエストを送信するオプションをキーと値のペアで指定します。
JavaScript日本語リファレンス
らしいです。これだけを見ると、初学者からすると非常にわかりづらいので、順々に追っています。
|
|
typeとurlは$ rails routes
ですぐにわかります。
Ajax関数がtypeとurlから判断して、コントローラのアクションへdataを送ります。
Ajaxによって送られたデータはコントローラ側ではparams
で受け取れます。
|
|
コントローラがrenderによって返したデータは.done(function (result)
のresult
に格納されます。
statusはresult.status
、jsonはresult.responseJSON
で取得できます。
簡単に図に表すとこんな感じです。
コントローラ側が特に何も返さなかったり、status: 200
などを返したときはdoneへ、エラーやstatus: 400
などを返したときはfailへ分岐します。
この辺りのイメージは、イラスト図解式 この一冊で全部わかるWeb技術の基本のChapter3を読むと理解が深まりました。
ダメなパターン
では、私がやらかしたことを以下に書きます。
status: 422を返さない(コントローラ側)
|
|
update
では保存に失敗したときにfalse
を返すだけでステータスを返さないため、js側では成功として認識します。そのため、.done
の方に処理が流れてしまいます。
update!
に変更するか(自動的に例外を返してくれる)、renderで明示的にstatusを返すようにすると、ちゃんと.fail
に流れてくれます。
ちなみに、headでヘッダのみのレスポンスを生成することもできるので、上のrender status: 200
はhead :created
と書くこともできます。
formとAjax関数で2つのリクエストを送ってしまう(js側)
|
|
このようにした場合、更新ボタンを押すと、
- フォームから送信されたデータ(通常のform_withのsubmitによるもの)
- Ajax関数によって送信されたデータ
と二重に送られてしまいます。
|
|
このようにe.preventDefault()と書くことで、通常の動作を制御することができます。
独立したイベントを入れ子にしてしまう(js側)
このときの私の思考:送信ボタンを押すというイベントは編集ボタンをクリックした後じゃないと実行しないから、この中に入れよう。
これに関しては、私のjsの理解度の低さが浮き彫りとなったミスだと思います。
|
|
何がダメかというと、送信ボタンを押した後も処理Aが終了しないのです。
図に示すと、このような入れ子型になります。
|
|
そのため、再び編集ボタンを押したときには、新たな処理Aが実行されます。その後更新ボタンを押すと、処理Bが2回実行されることになります。
(もちろん、その後同じことを繰り返すと3回、4回、……とどんどん膨れ上がります)
というわけで、一度のイベントで行われる処理同士は入れ子にせず、並列させるようにしましょう。
完成したコード
省略しているところもありますが、こんな感じです。
|
|
ちなみに、先述の並列の処理(編集ボタンを押したとき/キャンセルしたとき/送信したとき)は上のように.on
で繋げることができるそうです。