このページの内容は以下のリポジトリに1日遅れで反映されます(記事執筆前に前日分をコミットしています)。
https://github.com/hkob/hkob_blog
はじめに
Rails Tips の 85 回目です。コメントの追加・編集削除は articles#show の中に閉じています。この部分はページ遷移が必要ないので、TurboFrame 化してしまいましょう。
articles/show の修正
実際にarticles/show.html.haml
の Comments 以下を turbo_frame_tag の中に入れるだけです。実行すると確かに Turbo frame ないのみが更新されました。
- content_for :title do - @page_title = @article.title %p= @article.body - if @article.owned_by? current_user %ul %li= lotfp edit_article_path(@article) %li= destroy_lotfp article_path(@article), Article = turbo_frame_tag "comments" do %h2 Comments = render partial: "articles/comments/comment", collection: @comments - if user_signed_in? && @comment.new_record? %h2 Add a comment: = render "articles/comments/form"
turbo frame での flash 対応
TurboFrame は非常に便利なのですが、一箇所しか書き替えできないので、Flash メッセージは更新できません。Flash メッセージのためだけに Turbo Stream を使うのはもったいないので、TurboFrame でなんとかしたいと思っていました。調べていたら以下のようなページを見つけ、校務支援システムでも導入しています。
まず、application_controller.rb
に flash_turbo_frame
メソッドを作成し、after_action
に登録します。これは、turbo_frame のリクエストの時だけ、X-Flash-Messages ヘッダに flash 情報を登録するものになります。
class ApplicationController < ActionController::Base before_action :configure_permitted_parameters, if: :devise_controller? after_action :flash_turbo_frame def flash_turbo_frame return if response.redirect? message = {} if turbo_frame_request? message = flash.inject({}) do |hash, (type, _message)| # XSS対策&日本語のエスケープ hash[type] = CGI.escape("#{ERB::Util.html_escape(_message)}") hash end.to_json flash.discard end response.set_header('X-Flash-Messages', message) end
importmaps の設定
上のページを参考に X-Flash-Messages に登録されていた flash 情報を notice, alert の ID の場所に書き換える処理を行います。この部分は自分で作ってみました。app/javascripts/flash_messages.js
として保存しました。
document.addEventListener('turbo:before-fetch-response', function(event){ const json = JSON.parse(event.detail.fetchResponse.header("X-Flash-Messages")) let keys = ["notice", "alert"] keys.forEach(key => { let value = json[key] let element = document.getElementById(key) if (value) { element.innerHTML = decodeURI(value) element.classList.add(key) } else { element.innerHTML = "" element.classList.remove(key) } }) })
flash_messages.js を config/importmap.rb に pin 登録しました。
pin "flash_messages", to: "flash_messages.js", preload: true
この処理は全てのページで動作して欲しいので、 application.html.haml の最初に module 読み込みを行いました。
= javascript_importmap_tags = stylesheet_link_tag "application", "data-turbo-track": "reload" = javascript_import_module_tag "flash_messages"
おわりに
これで show の中にあるコメント追加・編集・削除は TurboFrame によりページ遷移せずに該当部分のみ描画されるようになりました。また、Flash を無理矢理書き換える処理も追加してみました。無事に動いているようです。