Help us understand the problem. What is going on with this article?

Rails 6.0.x標準で Ajax+(jQuery+Partial) でHTML部分更新する世界一シンプルなサンプル

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の部分差し替え


Rails 6 でFront EndがWebpack化されたことで,Rails 5 に比べてReactなども導入しやすくなっています.そのため,Back Endから返すCallbackはpre-renderしたHTMLでなく,Jsonなどでデータを返して,Front End側でrenderする,という方法もあります.

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)を追加します.

app/controller/ajax_test_controller.rb
class AjaxTestController < ApplicationController
  def top
    # NOP.
  end

  def update
    # TBD.
  end
end

top : Topページ表示用,特に処理はありません.
update : Ajaxリクエストを受ける用,実装はあとで追加します.

Route Config

ControllerのActionに繋げるためのRoute定義を追加します.

config/routes.rb
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を追加します.

app/views/ajax_test/top.html.erb
<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
にアクセスすると,↓ のようなページが表示されると思います.
default.png

ただし,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の部品を追加します.

app/views/ajax_test/_ajax_partial.html.erb
<div>
  Callback Msg = <%= results[:message] %>
</div>

このPartialで <div id="updated_by_ajax" > の中身を差し替えます.

Controller Action

さきほど作ったControllerの update の中身を実装します.

app/controller/ajax_test_controller.rb
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.erbajax_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の読み込み部分を追加します.

app/views/ajax_test/top.html.erb
<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を追加します.

app/javascript/packs/ajax_test.js
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

app/javascript/packs/application.js
// 他のrequireがいろいろ...

require("jquery") // 追加

config/webpack/environment.js
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 の文字列が更新されて ↓ のような画面になっていれば成功です.

ajax.png

ChromeのDeveloper ToolなどでDOM構造を見てみると,↓ のようになっていて,<div id="updated_by_ajax" >の中身が差し替わっているのが見えます.

dev_tool.png

おわり

Rails 6 でAjaxを使ったシーケンスを一本通すまでをできる限りDefaultのままで実現してみました.
環境依存でたまたまうまく動いている部分などあるかもしれません.もし何かおかしな点がありましたら教えていただけると助かります.

---///

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした