Action Text の概要

本ガイドでは、リッチテキストコンテンツの扱いを始めるのに必要なものをすべて提供します。

このガイドの内容:

  • Action Textの設定方法
  • リッチテキストコンテンツの扱い方
  • リッチテキストコンテンツにスタイルを付ける方法

1 はじめに

Action Textを使って、Railsにリッチテキストコンテンツと編集機能を導入できます。Action Textに含まれているTrixエディタは、書式設定/リンク/引用/リスト/画像埋め込み/ギャラリーなどあらゆるものを扱えます。 Trixエディタが生成するリッチテキストコンテンツは独自のRichTextモデルに保存され、このモデルはアプリケーションの既存のあらゆるActive Recordモデルと関連付けられます。 あらゆる埋め込み画像(およびその他の添付ファイル)は自動的にActive Storageに保存され、includeされたRichTextモデルに関連付けられます。

2 Trixと他のリッチテキストエディタを比較する

ほとんどのWYSIWYGエディタは、HTMLのcontenteditableexecCommand APIのラッパーです。これはマイクロソフトがInternet Explorer 5.5のライブエディット機能をサポートするために設計したもので、最終的にリバースエンジニアリングされて他のブラウザに普及しました。

これらのAPIの仕様やドキュメントは永遠に未完成のままであり、かつWYSIWYG HTMLエディタが扱う範囲が広大なため、ブラウザの実装ごとに独自のバグやおかしな動作が発生しています。ブラウザ間の動作のぶれの解決はJavaScript開発者たちに任せきりの状態です。

TrixではcontenteditableをI/Oデバイスとして扱うことで、こうしたブラウザ間の動作のぶれを回避しました。エディタに独自の方法で入力されると、Trixはその入力を内部のドキュメントモデル上での編集操作に変換してから、ドキュメントをエディタ上で再レンダリングします。これにより、Trixはあらゆるキーストロークで発生するものを完全に制御し、execCommandを使う必要性をすべて回避しています。

3 インストール

rails action_text:installを実行すると、Yarnパッケージが追加され、必要なマイグレーションがコピーされます。また、埋め込み画像や他の添付ファイルを扱うためにActive Storageのセットアップも必要です。詳しくはActive Storageの概要ガイドを参照してください。

Action Textでは、action_text_rich_textsテーブルとのポリモーフィックなリレーションシップを利用して、リッチテキスト属性を持つあらゆるモデルで共有できるようになっています。Action Textコンテンツを持つモデルがUUID値をidに使っている場合、Action Text属性を使うすべてのモデルで固有のidにUUID値を使う必要が生じます。また、Action Text用に生成されるマイグレーションでは、:recordreferences行にtype: :uuidを指定する形に更新する必要もあります。

インストールが完了すると、Railsアプリは以下のように変更されるはずです。

  1. JavaScriptのエントリポイントにtrix@rails/actiontextの両方を含める必要があります。

    // application.js
    import "trix"
    import "@rails/actiontext"
    
  2. trixスタイルシートは、自分のapplication.cssファイルでAction Textスタイルとともにインクルードされます。

4 リッチテキストコンテンツを作成する

既存のモデルにリッチテキストのフィールドを追加するには次のようにします。

# app/models/message.rb
class Message < ApplicationRecord
  has_rich_text :content
end

または、新しいモデルを生成するときに以下のようにリッチテキストフィールドを追加します。

bin/rails generate model Message content:rich_text

自分のmessagesテーブルにcontentフィールドを追加する必要はありません。

次に、フォーム内でモデルのこのフィールドをrich_text_areaを用いて参照します。

<%# app/views/messages/_form.html.erb %>
<%= form_with model: message do |form| %>
  <div class="field">
    <%= form.label :content %>
    <%= form.rich_text_area :content %>
  </div>
<% end %>

最後に、サニタイズ済みのリッチテキストをページ上に表示します。

<%= @message.content %>

リッチテキストコンテンツをコントローラで受け取れるようにするには、参照される属性を許可するだけで済みます。

class MessagesController < ApplicationController
  def create
    message = Message.create! params.require(:message).permit(:title, :content)
    redirect_to message
  end
end

5 リッチテキストコンテンツをレンダリングする

Action Textは、リッチテキストコンテンツのサニタイズやレンダリングを自動で行います。

デフォルトでは、Action TextエディタとコンテンツにはTrixのデフォルトのスタイルが与えられます。

このデフォルトを変更するには、application.scss// require "actiontext.scss"行を削除してtrix.cssが読み込まれないようにします。

デフォルトでは、Action Textは.trix-content CSSクラスを宣言した要素内でリッチテキストコンテンツをレンダリングします。

<%# app/views/layouts/action_text/contents/_content.html.erb %>
<div class="trix-content">
  <%= yield %>
</div>

リッチテキストを囲むHTMLを独自のレイアウトで変更するには、独自のapp/views/layouts/action_text/contents/_content.html.erbテンプレートを宣言してコンテンツ内でyieldを呼び出します。

画像やその他の添付ファイル(いわゆるblob: binary large object)の埋め込みに使うHTMLにもスタイルを指定できます。Action Textをインストールすると、app/views/active_storage/blobs/_blob.html.erbというパーシャルが配置されるので、ここでスタイルをカスタマイズできます。

5.1 添付ファイルのレンダリング

Action Textでは、Active Storage経由でアップロードした添付ファイルを埋め込むことも、署名済みグローバルIDで解決可能な任意のデータを埋め込むこともできます。

Action Textは、sgid属性を解決してインスタンス化することで埋め込み<action-text-attachment>をレンダリングします。解決が成功すると、そのインスタンスがrenderに渡されます。 生成されるHTMLは、<action-text-attachment>要素の子孫として埋め込まれます。

たとえばUserモデルを例に考えてみましょう。

# app/models/user.rb
class User < ApplicationRecord
  has_one_attached :avatar
end

user = User.find(1)
user.to_global_id.to_s #=> gid://MyRailsApp/User/1
user.to_signed_global_id.to_s #=> BAh7CEkiCG…

次に、 Userインスタンスにある署名済みグローバルIDを参照する<action-text-attachment>要素が埋め込まれたリッチテキストを考えてみましょう。

<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>.</p>

Action Textは、この"BAh7CEkiCG…"というStringを用いてUserインスタンスを解決します。

次に、アプリケーションのusers/userパーシャルを考えてみましょう。

<%# app/views/users/_user.html.erb %>
<span><%= image_tag user.avatar %> <%= user.name %></span>

Action TextでレンダリングされるHTMLは以下のようになります。

<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"><span><img src="..."> Jane Doe</span></action-text-attachment>.</p>

別のパーシャルをレンダリングするには、以下のようにUser#to_attachable_partial_pathを定義します。

class User < ApplicationRecord
  def to_attachable_partial_path
    "users/attachable"
  end
end

次にそのパーシャルを宣言します。Userインスタンスは、パーシャル内のuserローカル変数でアクセスできます。

<%# app/views/users/_attachable.html.erb %>
<span><%= image_tag user.avatar %> <%= user.name %></span>

Action Textの<action-text-attachment>要素レンダリングと統合するには、クラスが以下の条件を満たさなければなりません。

  • ActionText::Attachableモジュールをincludeする
  • #to_sgid(**options)を実装する(GlobalID::Identification concern)経由で利用可能)
  • (オプション)#to_attachable_partial_pathを宣言する

デフォルトでは、ActiveRecord::Baseのすべての子孫はGlobalID::Identification concernをミックスインするので、ActionText::Attachableと互換性があります。

6 N+1クエリを回避する

依存するActionText::RichTextをプリロードしたい場合は、以下のように名前付きスコープを利用できます(リッチテキストフィールド名がcontentという前提)。

Message.all.with_rich_text_content # 添付ファイルなしで本文をプリロードする
Message.all.with_rich_text_content_and_embeds # 本文と添付ファイルを両方プリロードする

7 APIとバックエンドの開発

  1. たとえばJSONを使うバックエンドAPIで、ファイルアップロード用に別のエンドポイントが必要だとします。このエンドポイントはActiveStorage::Blobを作成してそのattachable_sgidを返します。

    {
      "attachable_sgid": "BAh7CEkiCG…"
    }
    
  2. そのattachable_sgidを受け取ったら、<action-text-attachment>タグのあるリッチテキストコンテンツに挿入するようフロントエンド側に依頼します。

    <action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
    

これはBasecampをベースにしているので、詳しい情報についてはBasecampのドキュメントを参照してください。

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。よろしくお願いします。

YassLab 株式会社
https://yasslab.jp/

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。Railsガイドへの支援・協賛にご興味あれば info@yasslab.jp までお問い合わせください。

  1. Star
  2. このエントリーをはてなブックマークに追加