TL;DR (長い! 3行で!)
↓ の記事をRails 6.0.1 版に書き直したものです.
Rails 5.x標準で Ajax+(jQuery+Partial) でHTML部分更新する世界一シンプルなサンプル
Front EndがWebpackに変わっており,
・ Coffee Script → JavaScript への変更
・ jQuery導入方法の変更
が大きな変更点です.
動作確認環境
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.6 LTS ← 古いLTSなのでわりとダメ,できれば最新LTS使ってください
Release: 14.04
Codename: trusty
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
$ rails --version
Rails 6.0.1
$ yarn --version
1.16.0
$ nvm --version
0.35.1
$ node --version
v12.13.1
NOTICE
node
のversionが古いと(LTSじゃないと?),$ rails new
の途中で ↓ のようなErrorが出ます.
error get-caller-file@2.0.5: The engine "node" is incompatible with this module.
Expected version "6.* || 8.* || >= 10.*". Got "9.10.1"
error Found incompatible module.
これ以降,webpackが使えなくなります.
(error Command "webpack" not found.
)
$ rails new
自体は "Webpacker successfully installed" のメッセージがでて正常終了してるように見えてしまうため気付けない...
ざっくりシーケンス
だいたい ↓ のようなシーケンスを実現します.
Client-Server間でSession張って双方向通信とかそういうところはRails Frameworkが全部やってくれます.
Front End : UserがInputしたTextをAjaxでBack End側にPost
↓
Back End : PostされたTextとPartialを使って部分的に差し替えるHTMLをRendering
↓
Front End : JavaScriptでAjaxのCallback(部分的に差し替えるHTML)を受け取る
↓
Front End : jQueryでHTMLの部分差し替え
Project初期化 + Default動作確認
$ rails new ajax-test
でRails Projectを初期化します.$ cd ajax-test
でRailsのrootに移動して,
$ rails server
でサーバを起動して,
http://localhost:3000/
にアクセスして "Yay! You're on Rails!" のDefaultページが出たらOKです.
Static Page作成
Ajax動作のベースになるStatic Page部分を実装します.
Controller
$ rails generate controller AjaxTest
でControllerのTemplateを作成後,Action(Method)を追加します.
class AjaxTestController < ApplicationController
def top
# NOP.
end
def update
# TBD.
end
end
top
: Topページ表示用,特に処理はありません.
update
: Ajaxリクエストを受ける用,実装はあとで追加します.
Route Config
ControllerのActionに繋げるためのRoute定義を追加します.
get 'ajax_test/top', to: 'ajax_test#top', as: 'ajax_test_top'
post 'ajax_test/update', to: 'ajax_test#update', as: 'ajax_test_update'
GET
: Topページ表示用.
POST
: Ajaxリクエスト用.
View
ajax/test/top
にアクセスしたときに表示するViewを追加します.
<div id="request_ajax_update" >
<%= form_tag(ajax_test_update_path, method: :post, remote: true) { %>
<%= text_field :data, :text %>
<%= submit_tag 'Post AJAX' %>
<% } %>
</div>
<hr>
<div id="updated_by_ajax" >
DEFAULT
</div>
<hr>
大きく2つのブロックだけです.
・ <div id="request_ajax_update" >
Ajax RequestをPostするFormを持つブロック.
Userが入力したTextを params[:data][:text]
に詰めて,
ajax_test_update_path
( = /ajax_test/update
) にPOSTします.
・ <div id="updated_by_ajax" >
Ajax Callbackを受けてHTMLの部分更新をするブロック.
Static Page動作確認
この時点で,
http://localhost:3000/ajax_test/top
にアクセスすると,↓ のようなページが表示されると思います.
ただし,Ajaxの実装がまだ無いので,Post AJAX
ボタンをClickしても見た目上は何も起こりませんが,$ rails server
が動いているConsoleにText Fieldに入れた文字列がParametersとして通知されているのを確認できると思います.
Parameters: {"data"=>{"text"=>"Hello World !"}, "commit"=>"Post AJAX"}
Ajax実装
いままでに作ったStatic PageにAjax実装を組み込んでInteractiveな機能を追加します.
Partial View
部分的に更新するHTMLの部品を追加します.
<div>
Callback Msg = <%= results[:message] %>
</div>
このPartialで <div id="updated_by_ajax" >
の中身を差し替えます.
Controller Action
さきほど作ったControllerの update
の中身を実装します.
class AjaxTestController < ApplicationController
def top
# NOP.
end
def update
post_text = params[:data][:text]
results = { :message => post_text }
render partial: 'ajax_partial', locals: { :results => results }
end
end
Controllerに渡ってきた Parameters の中に格納されているUserが入力したTextを使ってPartialをrenderしています.
Partialのファイル名 _ajax_partial.html.erb
が ajax_partial
で使えるあたりはRailsの規約に沿っています.
ここで,
res = render_to_string partial: 'ajax_partial', locals: { :results => results }
puts res
のようなCodeを書いておくとPartialをrenderしたときの実際のOutputが見れます.
<div>
Callback Msg = Hello World !
</div>
JavaScript実装
Rails 6 からFront EndにWebpackが標準で使われることになり,Coffee ScriptがDefaultで使われなくなっています.
かわりに,
app/javascript/packs/application.js
のようなWebpackに対応したJavaScriptのDir構成に変わっています.
(Rails 5 でWebpack使っていた人にはおなじみの構成かも)
ViewにJavaScriptのEntry Pointを追加
ViewのHTMLにJavaScriptの読み込み部分を追加します.
<div id="request_ajax_update" >
<%= form_tag(ajax_test_update_path, method: :post, remote: true) { %>
<%= text_field :data, :text %>
<%= submit_tag 'Post AJAX' %>
<% } %>
</div>
<hr>
<div id="updated_by_ajax" >
DEFAULT
</div>
<hr>
<!-- ↓ 追加 -->
<%= javascript_pack_tag 'ajax_test' %>
<script>
register_callback();
</script>
<%= javascript_pack_tag 'ajax_test' %>
は app/javascript/packs/
以下に置いてある ajax_test
というファイル(の中身)を読み込む,という指示です.
実際にはWebpackがひとかたまりの.js
にしてしまうので,この名前のファイルを読み込んでいるわけではありません.
その後,<script></script>
タグで register_callback()
という関数を呼び出します.
この関数の実装は次で追加します.
JavaScript本体の実装
Webpack用Dir構成に沿って,Viewから呼び出せるように ↓ のJavaScriptを追加します.
import * as $ from "jquery";
function register_callback() {
$("#request_ajax_update").on(
"ajax:complete",
function(event) {
var res = event.detail[0].response
$('#updated_by_ajax').html(res)
}
);
}
window.register_callback = register_callback;
Viewから呼び出すregister_callback()
関数の中で,AjaxリクエストをPOSTする #request_ajax_update
Tagに ajax:complete
のCallback関数を登録しています.
Viewから呼び出せるようにするため,
window.register_callback = register_callbcak;
の行でwindow
の名前空間(global)にexportしています.
注) この方法は動作はしますがあまり行儀よくないはずです...
ただし,このままでTopページからPost AJAX
をClickしても何も起こりません.
ChromeのDeveloper Tool等でConsole Logをみると,
Uncaught Error: Cannot find module 'jquery'
at webpackMissingModule (ajax_test.js:1)
at Module../app/javascript/packs/ajax_test.js (ajax_test.js:1)
のようなError Logがでていて,jquery
の名前解決ができてないことがわかります.
jQueryを使えるようにする
Rails 5 から引き続きRails標準ではjQueryはサポートされていないため,WebpackからjQueryを使えるようにします.
$ yarn add jquery
のCommandでjqueryをInstallします.
必要かもしれない追加
"rails6 + jquery" とかのキーワードでぐぐると,↓ の2つの追加も必要,と出てきますが,手元の環境だと必要ありませんでした.
RailsのVersionによるのか,他の環境によるのか...
ref: https://www.botreetechnologies.com/blog/introducing-jquery-in-rails-6-using-webpacker
// 他のrequireがいろいろ...
require("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
Ajax動作確認
これで必要な変更はすべてです.
http://localhost:3000/ajax_test/top
にアクセスして,Text Fieldになにか文字列を入力してPost AJAX
ボタンを押したら,DEFAULT
の文字列が更新されて ↓ のような画面になっていれば成功です.
ChromeのDeveloper ToolなどでDOM構造を見てみると,↓ のようになっていて,<div id="updated_by_ajax" >
の中身が差し替わっているのが見えます.
おわり
Rails 6 でAjaxを使ったシーケンスを一本通すまでをできる限りDefaultのままで実現してみました.
環境依存でたまたまうまく動いている部分などあるかもしれません.もし何かおかしな点がありましたら教えていただけると助かります.
---///