**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.** Action Text Overview ==================== This guide provides you with all you need to get started in handling rich text content. After reading this guide, you will know: * What Action Text is, and how to install and configure it. * How to create, render, style, and customize rich text content. * How to handle attachments. -------------------------------------------------------------------------------- What is Action Text? -------------------- Action Text facilitates the handling and display of rich text content. Rich text content is text that includes formatting elements such as bold, italics, colors, and hyperlinks, providing a visually enhanced and structured presentation beyond plain text. It allows us to create rich text content, store it in a table, then attach it to any of our models. Action Text includes a [WYSIWYG](https://en.wikipedia.org/wiki/WYSIWYG) editor called Trix, which is used in web applications to provide users with a user-friendly interface for creating and editing rich text content. It handles everything from providing enriching capabilities like the formatting of text, adding links or quotes, embedding images, and much much more. You can view an example of the Trix editor [here](https://trix-editor.org/). The rich text content generated by the Trix editor is saved in its own RichText model that can be associated with any existing Active Record model in the application. In addition, any embedded images (or other attachments) can be automatically stored using Active Storage (which is added as a dependency) and associated with that RichText model. When it's time to render content, Action Text processes the content by sanitizing it first so that it's safe to embed directly into the page's HTML. INFO: Most WYSIWYG editors are wrappers around HTML’s `contenteditable` and `execCommand` APIs. These APIs were designed by Microsoft to support live editing of web pages in Internet Explorer 5.5. They were eventually reverse-engineered and copied by other browsers. Consequently, these APIs were never fully specified or documented, and because WYSIWYG HTML editors are enormous in scope, each browser's implementation has its own set of bugs and quirks. Hence, JavaScript developers are often left to resolve the inconsistencies.

Trix sidesteps these inconsistencies by treating `contenteditable` as an I/O device: when input makes its way to the editor, Trix converts that input into an editing operation on its internal document model, then re-renders that document back into the editor. This gives Trix complete control over what happens after every keystroke and avoids the need to use `execCommand` and the inconsistencies that come along with it. ## Installation To install Action Text and start working with rich text content, run: ```bash $ bin/rails action_text:install ``` It will do the following: - Installs the JavaScript packages for `trix` and `@rails/actiontext` and adds them to the `application.js`. - Adds the `image_processing` gem for analysis and transformations of the embedded images and other attachments with Active Storage. Please refer to the [Active Storage Overview](active_storage_overview.html) guide for more information about it. - Adds migrations to create the following tables that store rich text content and attachments: `action_text_rich_texts`, `active_storage_blobs`, `active_storage_attachments`, `active_storage_variant_records`. - Creates `actiontext.css` and imports it into `application.css`. The Trix stylesheet is also included in the `application.css` file. - Adds the default view partials `_content.html` and `_blob.html` to render Action Text content and Active Storage attachment (aka blob) respectively. Thereafter, executing the migrations will add the new `action_text_*` and `active_storage_*` tables to your app: ```bash $ bin/rails db:migrate ``` When the Action Text installation creates the `action_text_rich_texts` table, it uses a polymorphic relationship so that multiple models can add rich text attributes. This is done through the `record_type` and `record_id` columns, which store the ClassName of the model, and ID of the record, respectively. INFO: With polymorphic associations, a model can belong to more than one other model, on a single association. Read more about it in the [Active Record Associations guide](https://guides.rubyonrails.org/association_basics.html#polymorphic-associations). Hence, if your models containing Action Text content use UUID values as identifiers, then all models that use Action Text attributes will need to use UUID values for their unique identifiers. The generated migration for Action Text will also need to be updated to specify `type: :uuid` for the record references line. ```ruby t.references :record, null: false, polymorphic: true, index: false, type: :uuid ``` ## Creating Rich Text Content This section explores some of the configurations you'll need to follow to create rich text. The RichText record holds the content produced by the Trix editor in a serialized `body` attribute. It also holds all the references to the embedded files, which are stored using Active Storage. This record is then associated with the Active Record model which desires to have rich text content. The association is made by placing the `has_rich_text` class method in the model that you’d like to add rich text to. ```ruby # app/models/article.rb class Article < ApplicationRecord has_rich_text :content end ``` NOTE: There's no need to add the `content` column to your Article table. `has_rich_text` associates the content with the `action_text_rich_texts` table that has been created, and links it back to your model. You also may choose to name the attribute to be something different from `content`. Once you have added the `has_rich_text` class method to the model, you can then update your views to make use of the rich text editor (Trix) for that field. To do so, use a [`rich_text_area`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-rich_text_area) for the form field. ```html+erb <%# app/views/articles/_form.html.erb %> <%= form_with model: article do |form| %>
<%= form.label :content %> <%= form.rich_text_area :content %>
<% end %> ``` This will display a Trix editor that provides the functionality to create and update your rich text accordingly. Later we'll go into details about [how to update the styles for the editor](action_text_overview.html#removing-or-adding-trix-styles). Finally, to ensure that you can accept updates from the editor, you will need to permit the referenced attribute as a parameter in the relevant controller: ```ruby class ArticlesController < ApplicationController def create article = Article.create! params.require(:article).permit(:title, :content) redirect_to article end end ``` If the need arises to rename classes that utilize `has_rich_text`, you will also need to update the polymorphic type column `record_type` in the `action_text_rich_texts` table for the respective rows. Since Action Text depends on polymorphic associations, which, in turn, involve storing class names in the database, it's crucial to keep the data in sync with the class names used in your Ruby code. This synchronization is essential to maintain consistency between the stored data and the class references in your codebase. ## Rendering Rich Text Content Instances of `ActionText::RichText` can be directly embedded into a page because they have already sanitized their content for a safe render. You can display the content as follows: ```erb <%= @article.content %> ``` `ActionText::RichText#to_s` safely transforms RichText into an HTML String. On the other hand `ActionText::RichText#to_plain_text` returns a string that is not HTML safe and should not be rendered in browsers. You can learn more about Action Text's sanitization process in the [documentation](https://api.rubyonrails.org/classes/ActionText/RichText.html) for the `ActionText::RichText` class. NOTE: If there's an attached resource within `content` field, it might not show properly unless you have the necessary [dependencies for Active Storage](active_storage_overview.html#requirements) installed. ## Customizing the Rich Text Content Editor (Trix) There may be times when you want to update the presentation of the editor to meet your stylistic requirements, this section guides on how to do that. ### Removing or Adding Trix Styles By default, Action Text will render rich text content inside an element with the `.trix-content` class. This is set in `app/views/layouts/action_text/contents/_content.html.erb`. Elements with this class are then styled by the trix stylesheet. If you’d like to update any of the trix styles, you can add your custom styles in `app/assets/stylesheets/actiontext.css`. However, if you’d prefer to provide your own styles or utilize a third-party library instead of the default trix stylesheet, you can remove trix from the pre-processor directives in the `app/assets/stylesheets/actiontext.css` file by deleting the following: ```css = require trix ``` ### Customizing the Editor Container To customize the HTML container element that's rendered around rich text content, edit the `app/views/layouts/action_text/contents/_content.html.erb` layout file created by the installer: ```html+erb <%# app/views/layouts/action_text/contents/_content.html.erb %>
<%= yield %>
``` ### Customizing HTML for Embedded Images and Attachments To customize the HTML rendered for embedded images and other attachments (known as blobs), edit the `app/views/active_storage/blobs/_blob.html.erb` template created by the installer: ```html+erb <%# app/views/active_storage/blobs/_blob.html.erb %>
attachment--<%= blob.filename.extension %>"> <% if blob.representable? %> <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> <% end %>
<% if caption = blob.try(:caption) %> <%= caption %> <% else %> <%= blob.filename %> <%= number_to_human_size blob.byte_size %> <% end %>
``` ## Attachments Currently, Action Text supports attachments that are uploaded through Active Storage as well as attachments that are linked to a Signed GlobalID. ### Active Storage When uploading an image within your rich text editor, it uses Action Text which in turn uses Active Storage. However, Active Storage has [some dependencies](active_storage_overview.html#requirements) which are not provided by Rails. To use the built-in previewers, you must install these libraries. Some, but not all of these libraries are required and they are dependent on the kind of uploads you are expecting within the editor. A common error that users encounter when working with Action Text and Active Storage is that images do not render correctly in the editor. This is usually due to the `libvips` dependency not being installed. ### Signed GlobalID In addition to attachments uploaded through Active Storage, Action Text can also embed anything that can be resolved by a [Signed GlobalID](https://github.com/rails/globalid#signed-global-ids). A Global ID is an app-wide URI that uniquely identifies a model instance: `gid://YourApp/Some::Model/id`. This is helpful when you need a single identifier to reference different classes of objects. When using this method, Action Text requires attachments to have a signed global ID (sgid). By default, all Active Record models in a Rails app mix in the `GlobalID::Identification` concern, so they can be resolved by a signed global ID and are therefore `ActionText::Attachable` compatible. Action Text references the HTML you insert on save so that it can re-render with up-to-date content later on. This makes it so that you can reference models and always display the current content when those records change. Action Text will load up the model from the global ID and then render it with the default partial path when you render the content. An Action Text Attachment can look like this: ```html ``` Action Text renders embedded `` elements by resolving their sgid attribute of the element into an instance. Once resolved, that instance is passed along to a render helper. As a result, the HTML is embedded as a descendant of the `` element. To be rendered within Action Text `` element as an attachment, we must include the `ActionText::Attachable` module, which implements `#to_sgid(**options)` (made available through the `GlobalID::Identification` concern). You can also optionally declare `#to_attachable_partial_path` to render a custom partial path and `#to_missing_attachable_partial_path` for handling missing records. An example can be found here: ```ruby class Person < ApplicationRecord include ActionText::Attachable end person = Person.create! name: "Javan" html = %Q() content = ActionText::Content.new(html) content.attachables # => [person] ``` ### Rendering an Action Text Attachment The default way that an `` is rendered is through the default path partial. To illustrate this further, let’s consider a User model: ```ruby # 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… ``` NOTE: We can mix `GlobalID::Identification` into any model with a `.find(id)` class method. Support is automatically included in Active Record. The above code will return our identifier to uniquely identify a model instance. Next, consider some rich text content that embeds an `` element that references the User instance's signed GlobalID: ```html

Hello, .

``` Action Text uses the "BAh7CEkiCG…" String to resolve the User instance. It then renders it with the default partial path when you render the content. In this case, the default partial path is the `users/user` partial: ```html+erb <%# app/views/users/_user.html.erb %> <%= image_tag user.avatar %> <%= user.name %> ``` Hence, the resulting HTML rendered by Action Text would look something like: ```html

Hello, Jane Doe.

``` ### Rendering a Different Partial for the action-text-attachment To render a different partial for the attachable, define `User#to_attachable_partial_path`: ```ruby class User < ApplicationRecord def to_attachable_partial_path "users/attachable" end end ``` Then declare that partial. The User instance will be available as the user partial-local variable: ```html+erb <%# app/views/users/_attachable.html.erb %> <%= image_tag user.avatar %> <%= user.name %> ``` ### Rendering a Partial for an Unresolved Instance or Missing action-text-attachment If Action Text is unable to resolve the User instance (for example, if the record has been deleted), then a default fallback partial will be rendered. To render a different missing attachment partial, define a class-level `to_missing_attachable_partial_path` method: ```ruby class User < ApplicationRecord def self.to_missing_attachable_partial_path "users/missing_attachable" end end ``` Then declare that partial. ```html+erb <%# app/views/users/missing_attachable.html.erb %> Deleted user ``` ### Attachable via API If your architecture does not follow the traditional Rails server-side rendered pattern, then you may perhaps find yourself with a backend API (for example, using JSON) that will need a separate endpoint for uploading files. The endpoint will be required to create an `ActiveStorage::Blob` and return its `attachable_sgid`: ```json { "attachable_sgid": "BAh7CEkiCG…" } ``` Thereafter, you can take the `attachable_sgid` and insert it in rich text content within your frontend code using the `` tag: ```html ``` ## Miscellaneous ### Avoiding N+1 Queries If you wish to preload the dependent `ActionText::RichText` model, assuming your rich text field is named `content`, you can use the named scope: ```ruby Article.all.with_rich_text_content # Preload the body without attachments. Article.all.with_rich_text_content_and_embeds # Preload both body and attachments. ```