Slide 1

Slide 1 text

Hotwire を使って 管理画⾯を簡単にプチSPA化する Takaharu Yamamoto Kaigi on Rails 2023

Slide 2

Slide 2 text

⾃⼰紹介 ● ⼭本孝春 ● GitHub: yamataka22 ● 個⼈事業主 ● 中⼩規模システムの受託開発をしてます ● Railsで⼀気通貫した開発が好き

Slide 3

Slide 3 text

Hotwire 使ってますか?

Slide 4

Slide 4 text

Hotwire 使ってますか? 情報が少ない 実績が少ない とりあえず無効化

Slide 5

Slide 5 text

Hotwire 使ってますか? 情報が少ない 実績が少ない とりあえず無効化 Turbolinksの⼆の舞に...?!

Slide 6

Slide 6 text

本⽇のテーマ Hotwireを使って管理画⾯をモーダル化する →この説明を通じて、Hotwireの敷居を下げたい

Slide 7

Slide 7 text

● 話すこと ○ HotwireでModalを扱う⽅法を、コードを例⽰しながら実践 レベルで説明します ● 話さないこと ○ 時間の都合上、Hotwireの⼊⾨的な内容(TurboFrameとは 何か?など)は割愛します 今⽇の内容

Slide 8

Slide 8 text

管理画⾯にモーダルを 使うメリットは? そもそも

Slide 9

Slide 9 text

よくある画⾯遷移 ⼀覧で検索をして、⾏を選択

Slide 10

Slide 10 text

よくある画⾯遷移 詳細画⾯に遷移する

Slide 11

Slide 11 text

よくある画⾯遷移 そこから⼀覧に戻ると indexのPath

Slide 12

Slide 12 text

よくある画⾯遷移 検索状態がリセットされてしまう

Slide 13

Slide 13 text

よくある対策 ● ブラウザの別タブで表⽰ ○ assetsを含めた画⾯全体をリロードするので重い ○ 気づいたらブラウザがタブだらけ ● 戻るボタンに検索条件のパラメータを保持 ○ 戻るだけなのに再度検索が⾛る ○ スクロール位置などは保持されない

Slide 14

Slide 14 text

⼀覧からの遷移をモーダルで開けば

Slide 15

Slide 15 text

⼀覧の状態を維持できる

Slide 16

Slide 16 text

モーダル表⽰のメリット ● 画⾯の状態を維持しながら操作を進められる ● モーダル内だけ描画するので、挙動が軽い ● ユーザーはその画⾯に留まれる安⼼感がある

Slide 17

Slide 17 text

ただし管理画⾯なので ● 開発にそこまでコストをかけたくない ● サーバーサイド側のエンジニアだけで UIも含めて実装することが多い

Slide 18

Slide 18 text

ただし管理画⾯なので ● 開発にそこまでコストをかけたくない ● サーバーサイド側のエンジニアだけで UIも含めて実装することが多い モーダルのために作り込むことは避けたい

Slide 19

Slide 19 text

そこで Hotwire

Slide 20

Slide 20 text

Hotwireなら ● ほとんどJavaScriptを書くことなく ● ⼀般的な画⾯遷移版に少し⼿を⼊れるだけで ● 簡単にモーダル化できる

Slide 21

Slide 21 text

コードの説明の前に ● モーダルの描画にはBootstrapを利⽤します ○ Bootstrap以外でも、JavaScriptを使ってモーダル表⽰の制御ができれ ば、他のツールでも代替可能です ● 時間の都合上、細かな部分は説明を省略します ○ 本⽇説明するコードはGitHubで公開してます github.com/yamataka22/kaigionrails2023-admin-modal

Slide 22

Slide 22 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 23

Slide 23 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 24

Slide 24 text

モーダル表⽰のポイント ● TurboFrameを使って、モーダル部分のHTMLの破⽚を⽣ 成する ● その破⽚がブラウザに読み込まれたタイミングで、 Stimulusのインスタンスを⽣成し、connect()を経由して モーダルを開く

Slide 25

Slide 25 text

app/views/application/_remote_modal.html.erb Modal表⽰のerb (TurboFrame)

Slide 26

Slide 26 text

app/views/application/_remote_modal.html.erb 中⾝はBootstrapの⼀般的な Modal Component turbo_frame_tag で囲む Modal表⽰のerb (TurboFrame)

Slide 27

Slide 27 text

app/views/application/_remote_modal.html.erb ポイントはここ! 破⽚の中から、Stimulusの controllerを指定する この破⽚がブラウザに組み込まれたタイミングで 該当Stimulusのインスタンスが⽣成される →TurboFrameとStimulusを連携して、動きをつけられる Modal表⽰のerb (TurboFrame)

Slide 28

Slide 28 text

app/javascript/controllers/remote_modal_controller.js Modal表⽰のStimulus ● `connect()`は、Stimulusのインス タンスが⽣成されたときに⾃動的に 起動するメソッド ● TurboFrameで読み込んだHTMLに対 して、Stimulusで動きをつけられる ● `disconnect()`もある

Slide 29

Slide 29 text

これだけでモーダル表⽰できる 🎉

Slide 30

Slide 30 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 31

Slide 31 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 32

Slide 32 text

⼀般的な画⾯遷移による更新処理 編集フォームから更新すると

Slide 33

Slide 33 text

⼀般的な画⾯遷移による更新処理 エラーがあれば編集画⾯にとどまり

Slide 34

Slide 34 text

⼀般的な画⾯遷移による更新処理 更新に成功したら詳細画⾯にリダイレクトする

Slide 35

Slide 35 text

⼀般的な画⾯遷移による更新処理 更新に成功したら詳細画⾯にリダイレクトする モーダルでも同等な動きを実現したい

Slide 36

Slide 36 text

モーダルでの更新処理 編集フォームから更新すると

Slide 37

Slide 37 text

モーダルでの更新処理 エラーがあればモーダル上で表示され

Slide 38

Slide 38 text

モーダルでの更新処理 更新後に詳細画⾯が表⽰される

Slide 39

Slide 39 text

Controller#update app/controllers/products_controller.rb

Slide 40

Slide 40 text

Controller#update ● 更新に成功しても、redirectしない ● update.turbo_stream.erbがレンダリ ング対象 ● 本来redirectするパス (product_path) を@turbo_pathにセット →これは後述にて説明 app/controllers/products_controller.rb

Slide 41

Slide 41 text

Controller#update invalidの場合は :edit を再度レンダリン グする →これは通常の画⾯遷移時と同様 app/controllers/products_controller.rb

Slide 42

Slide 42 text

Turbo Stream app/views/products/update.turbo_stream.erb

Slide 43

Slide 43 text

Turbo Stream remote_modal の部分を @product で `replace` する (モーダルの内容が詳細画⾯に変わる) app/views/products/update.turbo_stream.erb

Slide 44

Slide 44 text

Turbo Stream app/views/products/update.turbo_stream.erb ⼀覧(index) に表⽰されているレコードの内容も 最新化する

Slide 45

Slide 45 text

以上で登録後の遷移ができる

Slide 46

Slide 46 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 47

Slide 47 text

やること 1. TurboFrame + Stimulus でモーダルを開く 2. TurboStream で登録後の描画を切り替える 3. ブラウザのURLを追随させる

Slide 48

Slide 48 text

管理画⾯の運⽤でよくあるシーン URLを使ってやり取りすることが多々ある 例えばSlackで...

Slide 49

Slide 49 text

管理画⾯の運⽤でよくあるシーン URLを使ってやり取りすることが多々ある 例えばSlackで... こんな感じで該当ページのURLを使って コミュニケーションしたい

Slide 50

Slide 50 text

単にModal化しただけでは ブラウザのURLが追随しない →URLを使った運⽤ができない 管理画⾯の実運⽤も考慮して 対処したい

Slide 51

Slide 51 text

2つの対策を提案 a) モーダル起動の link_to に `turbo_action: :advance` を指定 b) JavaScriptの History API を使って制御する

Slide 52

Slide 52 text

2つの対策を提案 a) モーダル起動の link_to に `turbo_action: :advance` を指定 ∙ Railsの仕組みでできるのでシンプル ∙ Modalを閉じたときや、更新後の表⽰切り替え時に追随しない b) JavaScriptの History API を使って制御する ∙ URLのために仕組みを実装する必要 ∙ Modalを閉じたとき、更新後の表⽰切り替えも追随できる

Slide 53

Slide 53 text

2つの対策を提案 a) モーダル起動の link_to に `turbo_action: :advance` を指定 ∙ Railsの仕組みでできるのでシンプル ∙ Modalを閉じたときや、更新後の表⽰切り替え時に追随しない b) JavaScriptの History API を使って制御する ∙ URLのために仕組みを実装する必要 ∙ Modalを閉じたとき、更新後の表⽰切り替えも追随できる 今回はこちらの内容を説明

Slide 54

Slide 54 text

以下2つのURLを使う ● Modalを表⽰したときのURL ○ TurboFrameによるGETリクエスト or リダイレクトのURL ○ Modalを開く際に、HistoryAPIにセットする ● Modalを閉じたときのURL ○ Modalを開く直前のURLを location.href から取得しておく ○ Modalのhideイベントをフックして、このURLに戻す

Slide 55

Slide 55 text

app/controllers/application_controller.rb ApplicationControllerの before_action で、turboでリクエスト されたURLを @turbo_path へセットし ておく Modal起動時のURL GETリクエストでModalを起動する

Slide 56

Slide 56 text

app/controllers/products_controller.rb 例えば #update に成功すると、通常の画⾯遷移版 では show_path へリダイレクトする。 そのpathを @turbo_path としてセットしておく。 Modal起動時のURL Modalにリダイレクトの動きをさせたい場合

Slide 57

Slide 57 text

app/views/application/_remote_modal.html.erb @turbo_path をModalに付与しておき、Stimulusを介 してHistoyAPIにセットする 前項で説明した turbo_frame でModal表示をするerb

Slide 58

Slide 58 text

app/javascript/controllers/remote_modal_controller.js 前述で説明した、Stimulusのconnect()を使って Modalを開くロジック部分 Modalを起動するStimulus

Slide 59

Slide 59 text

Modalを開く前に、URLの仕組みを ⼊れる app/javascript/controllers/remote_modal_controller.js Modalを起動するStimulus

Slide 60

Slide 60 text

app/javascript/controllers/remote_modal_controller.js Modalを起動するStimulus 前述の `turboPath` を HistoryAPIにセットするこ とで、Modal起動時にURLを追随させる

Slide 61

Slide 61 text

現在のパス=Modalを開く直前のパ ス (`location.href`) を、Modal#hide イベントをフックしてセットする app/javascript/controllers/remote_modal_controller.js Modalを起動するStimulus

Slide 62

Slide 62 text

以上でブラウザ上のURLが追随します

Slide 63

Slide 63 text

ちなみに、この状態で URLを直接指定したリクエストをすると...

Slide 64

Slide 64 text

特に何も仕組みを設けなくても表⽰できる

Slide 65

Slide 65 text

コード補足 a) 登録フォームのModalは、背景クリックでも閉じないように ● ⼊⼒途中にうっかりModalが閉じることを防ぐ ● 詳細ページなら背景クリックで閉じたほうが便利 b) Flashによるメッセージ表⽰ ● Turboだと少し仕組みを設ける必要 サンプルのGitHubリポジトリで実装しています github.com/yamataka22/kaigionrails2023-admin-modal

Slide 66

Slide 66 text

まとめ ● Hotwireを使って、管理画⾯をModalでプチSPA化するポイントを説明 しました ○ TurboFrameとStimulus (connect()) を連携させて、HTMLの破⽚に動き をつける ○ TurboStreamで、Modal上の描画を切り替える(リダイレクトのイメー ジ) ● 今回説明した内容は、管理画⾯はもちろん、⼀般的なフロント向け ページでも応⽤可能です

Slide 67

Slide 67 text

ご清聴ありがとうございました ※ご不明な点などあれば、お気軽にご連絡ください github.com/yamataka22