Обзор Action Text

Это руководство предоставляет всё, что нужно для начала работы с содержимым обогащённого текста (rich text): установка и настройка Action Text, создание и отображение контента, работа с вложениями.

Это руководство предоставляет всё, что нужно для начала работы с содержимым обогащённого текста.

После прочтения этого руководства, вы узнаете:

  • Что такое Action Text, как его установить и настроить.
  • Как создавать, отрисовывать, стилизовать и кастомизировать содержимое обогащённого текста.
  • Как работать с вложениями.

Что такое Action Text?

Action Text упрощает обработку и отображение содержимого обогащённого текста. Под содержимым обогащённого текста понимается текст, включающий элементы форматирования, такие как жирный шрифт, курсив, цвета и гиперссылки, обеспечивающий визуально улучшенное и структурированное представление в сравнении с простым текстом. Он позволяет создавать содержимое обогащённого текста, сохранять его в таблице и прикреплять к любым моделям.

Action Text включает редактор WYSIWYG под названием Trix, который используется в веб-приложениях, чтобы предоставить пользователям удобный интерфейс для создания и редактирования содержимого обогащённого текста. Он обеспечивает всё: от возможностей форматирования текста, добавления ссылок или цитат, встраивания изображений и многого, многого другого. Примеры можно посмотреть на сайте редактора Trix.

Содержимое обогащённого текста, созданное редактором Trix, сохраняется в собственной модели RichText, которая может быть ассоциирована с любой существующей моделью Active Record в приложении. Кроме того, любые встроенные изображения (или другие вложения) могут автоматически храниться с помощью Active Storage (который добавляется как зависимость) и связываться с этой моделью RichText. Когда настаёт время отрисовать содержимое, Action Text обрабатывает его, сначала очищая (sanitize), чтобы было безопасно встраивать непосредственно в HTML страницы.

Большинство редакторов WYSIWYG представляют собой обёртки вокруг HTML API contenteditable и execCommand. Эти API были разработаны Microsoft для поддержки живого редактирования веб-страниц в Internet Explorer 5.5. В итоге они были реверс-инжинирены и скопированы другими браузерами. Следовательно, эти API никогда не были полностью специфицированы или задокументированы, а поскольку WYSIWYG HTML-редакторы имеют огромный объём, в реализации каждого браузера есть свой набор ошибок и странностей. Поэтому JavaScript-разработчики часто остаются один на один с проблемами несоответствий.

Trix обходит эти несоответствия, относясь к contenteditable как к устройству ввода-вывода: когда ввод попадает в редактор, Trix преобразует его в операцию редактирования внутренней модели документа, а затем повторно отрисовывает этот документ обратно в редакторе. Это даёт Trix полный контроль над тем, что происходит после каждого нажатия клавиши, и позволяет избежать использования execCommand и связанных с ним несоответствий.

Установка

Чтобы установить Action Text и начать работать с содержимым обогащённого текста, запустите:

$ bin/rails action_text:install

Это выполнит следующие действия:

  • Установит JavaScript-пакеты для trix и @rails/actiontext и добавит их в application.js.
  • Добавит гем image_processing для анализа и преобразования встроенных изображений и других вложений с помощью Active Storage. За подробностями обратитесь к руководству Обзор Active Storage.
  • Добавит миграции для создания следующих таблиц, хранящих содержимое обогащённого текста и вложения: action_text_rich_texts, active_storage_blobs, active_storage_attachments, active_storage_variant_records.
  • Создаст actiontext.css, включающий все стили Trix и переопределения.
  • Добавит стандартные партиалы вью _content.html и _blob.html для отрисовки содержимого Action Text и вложений Active Storage (то есть blob) соответственно.

После этого выполнение миграций добавит в ваше приложение новые таблицы action_text_* и active_storage_*:

$ bin/rails db:migrate

При установке Action Text создаёт таблицу action_text_rich_texts, используя полиморфную связь, благодаря чему несколько моделей могут добавлять атрибуты обогащённого текста. Это делается через столбцы record_type и record_id, которые хранят соответственно имя класса модели и ID записи.

В случае полиморфных связей одна модель может принадлежать более чем одной другой модели через одну ассоциацию. Подробнее об этом читайте в руководстве по связям Active Record.

Следовательно, если ваши модели, содержащие контент Action Text, используют значения UUID в качестве идентификаторов, тогда все модели, использующие атрибуты Action Text, должны использовать UUID-значения для своих уникальных идентификаторов. Сгенерированную миграцию для Action Text также необходимо обновить, указав type: :uuid в строчке ссылки на запись.

t.references :record, null: false, polymorphic: true, index: false, type: :uuid

Создание содержимого обогащённого текста

В этом разделе рассматриваются некоторые конфигурации, необходимые для создания обогащённого текста.

Запись RichText хранит содержимое, созданное редактором Trix, в сериализованном атрибуте body. Она также хранит все ссылки на встроенные файлы, которые хранятся через Active Storage. Эта запись затем ассоциируется с моделью Active Record, которой нужно иметь содержимое обогащённого текста. Связь устанавливается путём размещения метода класса has_rich_text в той модели, к которой вы хотите добавить обогащённый текст.

# app/models/article.rb
class Article < ApplicationRecord
  has_rich_text :content
end

NOTE: Нет необходимости добавлять столбец content в вашу таблицу Article. has_rich_text ассоциирует содержимое с таблицей action_text_rich_texts, которая была создана, и связывает её обратно с вашей моделью. Вы также можете дать атрибуту любое другое имя, отличное от content.

После того как метод класса has_rich_text добавлен в модель, можно обновить вью, чтобы использовать редактор обогащённого текста (Trix) для этого поля. Для этого используйте rich_textarea для поля формы.

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

Это отобразит редактор Trix, который предоставляет функциональность для создания и обновления вашего обогащённого текста. Позже мы подробно рассмотрим, как обновить стили для редактора.

Наконец, чтобы убедиться, что вы можете принимать обновления от редактора, нужно разрешить ссылаемый атрибут как параметр в соответствующем контроллере:

class ArticlesController < ApplicationController
  def create
    article = Article.create! params.expect(article: [:title, :content])
    redirect_to article
  end
end

Если возникнет необходимость переименовать классы, использующие has_rich_text, вам также понадобится обновить столбец полиморфного типа record_type в таблице action_text_rich_texts для соответствующих строк.

Поскольку Action Text зависит от полиморфных связей, которые, в свою очередь, подразумевают хранение имён классов в базе данных, крайне важно поддерживать соответствие данных и имён классов, используемых в вашем Ruby-коде. Эта синхронизация необходима для поддержания целостности между хранящимися данными и ссылками на классы в вашей кодовой базе.

Отрисовка содержимого обогащённого текста

Экземпляры ActionText::RichText можно напрямую встраивать в страницу, поскольку их содержимое уже очищено для безопасной отрисовки. Отобразить содержимое можно так:

<%= @article.content %>

ActionText::RichText#to_s безопасно преобразует RichText в HTML-строку. С другой стороны, ActionText::RichText#to_plain_text возвращает строку, не являющуюся HTML-безопасной, и её не следует отрисовывать в браузерах без дополнительной очистки. Подробнее о процессе очистки в Action Text можно узнать в документации ActionText::RichText.

NOTE: Если в поле content есть прикреплённый ресурс, он может отображаться неправильно, если у вас не установлены необходимые зависимости для Active Storage.

Кастомизация редактора содержимого обогащённого текста (Trix)

Возможно, вам потребуется обновить внешний вид редактора, чтобы он соответствовал вашим стилистическим требованиям. В этом разделе рассказывается, как это сделать.

Удаление или добавление стилей Trix

По умолчанию Action Text отрисует содержимое обогащённого текста внутри элемента с классом .trix-content. Это установлено в app/views/layouts/action_text/contents/_content.html.erb. Элементы с этим классом стилизуются с помощью таблицы стилей trix.

Если вы хотите обновить любые стили trix, можно добавить свои собственные стили в app/assets/stylesheets/actiontext.css, который включает как полный набор стилей для Trix, так и переопределения, необходимые для Action Text.

Кастомизация контейнера редактора

Чтобы кастомизировать HTML-элемент контейнера, отрисовываемый вокруг содержимого обогащённого текста, отредактируйте файл макета app/views/layouts/action_text/contents/_content.html.erb, созданный установщиком:

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

Кастомизация HTML для встроенных изображений и вложений

Чтобы кастомизировать HTML, отрисовываемый для встроенных изображений и других вложений (известных как blob), отредактируйте шаблон app/views/active_storage/blobs/_blob.html.erb, созданный установщиком:

<%# app/views/active_storage/blobs/_blob.html.erb %>
<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
  <% if blob.representable? %>
    <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
  <% end %>

  <figcaption class="attachment__caption">
    <% if caption = blob.try(:caption) %>
      <%= caption %>
    <% else %>
      <span class="attachment__name"><%= blob.filename %></span>
      <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
    <% end %>
  </figcaption>
</figure>

Вложения

В настоящее время Action Text поддерживает вложения, загруженные через Active Storage, а также вложения, связанные с Signed GlobalID.

Active Storage

Когда вы загружаете изображение через редактор обогащённого текста, он использует Action Text, который, в свою очередь, использует Active Storage. Однако, Active Storage имеет некоторые зависимости, которые не предоставляются Rails. Чтобы использовать встроенные средства предпросмотра, необходимо установить эти библиотеки.

Некоторые, но не все из этих библиотек обязательны, и их выбор зависит от того, какие типы загрузок вы ожидаете в редакторе. Распространённая ошибка, с которой сталкиваются пользователи при работе с Action Text и Active Storage, заключается в том, что изображения отображаются некорректно в редакторе. Обычно это связано с отсутствием установленной зависимости libvips.

JavaScript-события прямой загрузки вложений

Action Text отправляет события прямой загрузки Active Storage в течение жизненного цикла прикрепления файла.

В дополнение к типичным свойствам event.detail, Action Text также отправляет события со свойством event.detail.attachment.

Имя событияЦель событияДанные события (event.detail)Описание
direct-upload:initialize<trix-editor>{id, file, attachment}Отправляется для каждого файла после отправки формы.
direct-upload:start<trix-editor>{id, file, attachment}Прямая загрузка начинается.
direct-upload:before-blob-request<trix-editor>{id, file, xhr, attachment}Перед выполнением запроса в ваше приложение за метаданными прямой загрузки.
direct-upload:before-storage-request<trix-editor>{id, file, xhr, attachment}Перед выполнением запроса на сохранение файла.
direct-upload:progress<trix-editor>{id, file, progress, attachment}По мере прогресса запросов на сохранение файлов.
direct-upload:error<trix-editor>{id, file, error, attachment}Произошла ошибка. Будет показан alert, если событие не отменить.
direct-upload:end<trix-editor>{id, file, attachment}Прямая загрузка завершилась.

NOTE: Возможна ситуация, при которой файлы, загруженные Action Text через прямые загрузки Active Storage, никогда не будут встроены в содержимое обогащённого текста. Рассмотрите регулярную очистку неприкреплённых загрузок.

Signed GlobalID

В дополнение к вложениям, загруженным через Active Storage, Action Text также может встраивать всё, что может быть разрешено по Signed GlobalID.

Global ID — это URI на уровне всего приложения, который уникально идентифицирует экземпляр модели: gid://YourApp/Some::Model/id. Это полезно, когда нужен единый идентификатор для ссылок на объекты разных классов.

При использовании этого метода Action Text требует, чтобы у вложений был подписанный глобальный идентификатор (sgid). По умолчанию все модели Active Record в приложении Rails включают модуль GlobalID::Identification, поэтому они могут быть разрешены по подписанному глобальному идентификатору и, следовательно, совместимы с ActionText::Attachable.

Action Text ссылается на HTML, который вы вставляете при сохранении, чтобы позднее можно было повторно отрисовать его с актуальным содержимым. Это даёт возможность ссылаться на модели и всегда отображать актуальное содержимое при изменении этих записей.

Action Text загрузит модель по global ID, а затем отрисует её по пути партиала по умолчанию, когда вы отрисовываете содержимое.

Вложение Action Text может выглядеть так:

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

Action Text отрисовывает встроенные элементы <action-text-attachment>, разрешая их атрибут sgid в экземпляр. После разрешения этот экземпляр передаётся в render-хелпер. В результате HTML встраивается как потомок элемента <action-text-attachment>.

Чтобы отрисовываться в элементе <action-text-attachment> Action Text как вложение, мы должны включить модуль ActionText::Attachable, который реализует #to_sgid(**options) (доступный через модуль GlobalID::Identification).

Опционально также можно объявить #to_attachable_partial_path, чтобы отрисовывать кастомный путь партиала, и #to_missing_attachable_partial_path для обработки отсутствующих записей.

Пример приведён ниже:

class Person < ApplicationRecord
  include ActionText::Attachable
end

person = Person.create! name: "Javan"
html = %Q(<action-text-attachment sgid="#{person.attachable_sgid}"></action-text-attachment>)
content = ActionText::Content.new(html)
content.attachables # => [person]

Отрисовка вложения Action Text

Стандартный способ отрисовки <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…

NOTE: Мы можем подмешать GlobalID::Identification в любую модель с методом класса .find(id). Поддержка автоматически включена в Active Record.

Приведённый выше код вернёт наш идентификатор, уникально идентифицирующий экземпляр модели.

Далее рассмотрим некоторое содержимое обогащённого текста, которое встраивает элемент <action-text-attachment>, ссылающийся на подписанный GlobalID экземпляра User:

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

Action Text использует строку "BAh7CEkiCG…" для разрешения экземпляра User. Затем он отрисовывает его по пути партиала по умолчанию при отрисовке содержимого.

В этом случае путь партиала по умолчанию — users/user:

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

Следовательно, результирующий HTML, отрисованный Action Text, будет выглядеть примерно так:

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

Отрисовка другого партиала для action-text-attachment

Чтобы отрисовать другой партиал для вложения, определите 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-attachment

Если Action Text не может разрешить экземпляр User (например, если запись была удалена), то будет отрисован резервный партиал по умолчанию.

Чтобы отрисовать другой партиал для отсутствующего вложения, определите метод уровня класса to_missing_attachable_partial_path:

class User < ApplicationRecord
  def self.to_missing_attachable_partial_path
    "users/missing_attachable"
  end
end

Затем объявите этот партиал.

<%# app/views/users/missing_attachable.html.erb %>
<span>Deleted user</span>

Прикрепление через API

Если ваша архитектура не следует традиционному шаблону Rails с серверной отрисовкой, тогда, возможно, у вас будет бэкенд API (например, использующий JSON), которому потребуется отдельная точка доступа для загрузки файлов. Эта точка доступа должна создать ActiveStorage::Blob и вернуть его attachable_sgid:

{
  "attachable_sgid": "BAh7CEkiCG…"
}

После этого вы можете взять attachable_sgid и вставить его в содержимое обогащённого текста в коде вашего фронтенда с помощью тега <action-text-attachment>:

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

Прочее

Избегание N+1 запросов

Если вы хотите предзагрузить зависимую модель ActionText::RichText, при условии, что ваше поле обогащённого текста называется content, можно использовать именованный scope:

Article.all.with_rich_text_content # Предзагрузка тела без вложений.
Article.all.with_rich_text_content_and_embeds # Предзагрузка тела и вложений.

On this page