1 什麼是 Action Text?
Action Text 有助於處理和顯示豐富文字內容。豐富文字內容是包含格式化元素的文字,例如粗體、斜體、色彩和超連結,提供超越純文字的視覺增強和結構化呈現。它允許我們建立豐富文字內容、將其儲存在表格中,然後將其附加到我們的任何模型。
Action Text 包含一個名為 Trix 的 所見即所得編輯器,該編輯器在 Web 應用程式中用於為使用者提供一個使用者友善的介面,以建立和編輯豐富文字內容。它處理所有事情,從提供豐富的功能,如文字格式化、新增連結或引號、嵌入影像,以及更多。請參閱 Trix 編輯器網站以取得範例。
由 Trix 編輯器產生的豐富文字內容會儲存在它自己的 RichText 模型中,該模型可以與應用程式中的任何現有 Active Record 模型相關聯。此外,任何嵌入的影像(或其他附件)都可以使用 Active Storage (已新增為相依性) 自動儲存,並與該 RichText 模型相關聯。當需要渲染內容時,Action Text 會先清理內容,使其可以安全地直接嵌入到頁面的 HTML 中。
大多數所見即所得編輯器都是 HTML 的 contenteditable
和 execCommand
API 的包裝器。這些 API 由 Microsoft 設計,以支援 Internet Explorer 5.5 中網頁的即時編輯。它們最終被其他瀏覽器逆向工程和複製。因此,這些 API 從未被完全指定或記錄下來,而且由於所見即所得的 HTML 編輯器的範圍非常廣泛,因此每個瀏覽器的實作都有自己的一組錯誤和怪癖。因此,JavaScript 開發人員常常必須解決這些不一致的問題。
Trix 透過將 contenteditable
視為 I/O 裝置來規避這些不一致性:當輸入進入編輯器時,Trix 會將該輸入轉換為對其內部文件模型的編輯操作,然後將該文件重新渲染回編輯器。這使 Trix 可以完全控制每次按鍵後發生的事情,並避免需要使用 execCommand
以及隨之而來的不一致性。
2 安裝
若要安裝 Action Text 並開始處理豐富文字內容,請執行
$ bin/rails action_text:install
它將執行以下操作
- 安裝
trix
和@rails/actiontext
的 JavaScript 套件,並將它們新增至application.js
。 - 新增
image_processing
gem 以分析和轉換使用 Active Storage 的嵌入影像和其他附件。請參閱 Active Storage 概觀指南以取得更多資訊。 - 新增遷移以建立儲存豐富文字內容和附件的以下表格:
action_text_rich_texts
、active_storage_blobs
、active_storage_attachments
、active_storage_variant_records
。 - 建立包含所有 Trix 樣式和覆寫的
actiontext.css
。 - 新增預設視圖局部視圖
_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
資料行完成的,它們分別儲存模型的 ClassName 和記錄的 ID。
使用多型關聯,一個模型可以在單一關聯上屬於多個其他模型。請在 Active Record 關聯指南中閱讀更多關於它的資訊。
因此,如果包含 Action Text 內容的模型使用 UUID 值作為識別碼,則所有使用 Action Text 屬性的模型都需要使用 UUID 值作為其唯一識別碼。Action Text 的產生遷移也需要更新以針對記錄參照行指定 type: :uuid
。
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
3 建立豐富文字內容
本節將探討您需要遵循的一些設定,以建立豐富文字。
RichText 記錄在其序列化的 body
屬性中保存 Trix 編輯器產生的內容。它還保存對使用 Active Storage 儲存的嵌入式檔案的所有參考。然後,此記錄會與希望具有豐富文字內容的 Active Record 模型相關聯。關聯是透過將 has_rich_text
類別方法放置在您想要新增豐富文字的模型中來完成的。
# app/models/article.rb
class Article < ApplicationRecord
has_rich_text :content
end
不需要將 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
的類別,您也需要更新 action_text_rich_texts
表格中各自列的多型類型資料行 record_type
。
由於 Action Text 依賴多型關聯,而多型關聯又涉及在資料庫中儲存類別名稱,因此務必使資料與您的 Ruby 程式碼中使用的類別名稱保持同步。此同步對於維護儲存資料和您程式碼中的類別參考之間的一致性至關重要。
4 渲染豐富文字內容
ActionText::RichText
的實例可以直接嵌入到頁面中,因為它們已經清理了它們的內容以進行安全渲染。您可以如下顯示內容
<%= @article.content %>
ActionText::RichText#to_s
安全地將 RichText 轉換為 HTML 字串。另一方面,ActionText::RichText#to_plain_text
會傳回一個不是 HTML 安全的字串,不應在瀏覽器中渲染。您可以在 ActionText::RichText
文件中了解更多關於 Action Text 清理過程的資訊。
如果 content
欄位中有附加的資源,除非您安裝了必要的 Active Storage 相依性,否則它可能無法正確顯示。
5 自訂豐富文字內容編輯器 (Trix)
有時候您可能想要更新編輯器的呈現方式,以符合您的樣式需求,本節將指導您如何進行操作。
5.1 移除或新增 Trix 樣式
預設情況下,Action Text 會在具有 .trix-content
類別的元素內呈現富文本內容。此設定位於 app/views/layouts/action_text/contents/_content.html.erb
中。具有此類別的元素會套用 trix 樣式表中的樣式。
如果您想更新任何 trix 樣式,可以在 app/assets/stylesheets/actiontext.css
中新增您的自訂樣式,其中包含 Trix 的完整樣式集以及 Action Text 所需的覆寫。
5.2 自訂編輯器容器
若要自訂環繞富文本內容呈現的 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>
5.3 自訂嵌入圖片和附件的 HTML
若要自訂為嵌入圖片和其他附件(稱為 blob)呈現的 HTML,請編輯安裝程式建立的 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>
6 附件
目前,Action Text 支援透過 Active Storage 上傳的附件,以及連結到 Signed GlobalID 的附件。
6.1 Active Storage
當您在富文本編輯器中上傳圖片時,它會使用 Action Text,而 Action Text 又會使用 Active Storage。但是,Active Storage 有一些 Rails 沒有提供的相依性。若要使用內建的預覽器,您必須安裝這些函式庫。
其中某些(但並非全部)函式庫是必要的,它們取決於您在編輯器中預期的上傳類型。使用者在使用 Action Text 和 Active Storage 時遇到的一個常見錯誤是圖片在編輯器中無法正確呈現。這通常是因為未安裝 libvips
相依性。
6.1.1 附件直接上傳 JavaScript 事件
事件名稱 | 事件目標 | 事件資料 (event.detail ) |
描述 |
---|---|---|---|
direct-upload:start |
<input> |
{id, file} |
直接上傳正在開始。 |
direct-upload:progress |
<input> |
{id, file, progress} |
在儲存檔案的請求進行時。 |
direct-upload:error |
<input> |
{id, file, error} |
發生錯誤。除非取消此事件,否則會顯示 alert 。 |
direct-upload:end |
<input> |
{id, file} |
直接上傳已結束。 |
6.2 Signed GlobalID
除了透過 Active Storage 上傳的附件之外,Action Text 也可以嵌入任何可由 Signed GlobalID 解析的內容。
Global ID 是應用程式範圍內的 URI,可唯一識別模型實例:gid://YourApp/Some::Model/id
。當您需要單一識別碼來參考不同類別的物件時,這會很有幫助。
使用此方法時,Action Text 要求附件具有 signed global ID (sgid)。預設情況下,Rails 應用程式中的所有 Active Record 模型都會混合使用 GlobalID::Identification
關注點,因此它們可以透過 signed global ID 解析,因此與 ActionText::Attachable
相容。
Action Text 會參考您在儲存時插入的 HTML,以便稍後可以使用最新的內容重新呈現。這使得您可以參考模型,並在這些記錄變更時始終顯示目前的內容。
Action Text 會從 global ID 載入模型,然後在您呈現內容時使用預設部分路徑呈現模型。
Action Text 附件可能如下所示
<action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
Action Text 會藉由將元素的 sgid 屬性解析為實例來呈現嵌入的 <action-text-attachment>
元素。一旦解析完成,該實例會傳遞給呈現輔助程式。因此,HTML 會以 <action-text-attachment>
元素的後代形式嵌入。
為了在 Action Text 的 <action-text-attachment>
元素中以附件的形式呈現,我們必須包含 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]
6.3 呈現 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…
我們可以將 GlobalID::Identification
混合到任何具有 .find(id)
類別方法的模型中。Active Record 中會自動包含支援。
上述程式碼將會傳回我們的識別碼,以唯一識別模型實例。
接下來,請考慮一些富文本內容,其中嵌入了參考 User 實例的 signed GlobalID 的 <action-text-attachment>
元素
<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>
因此,Action Text 呈現的結果 HTML 看起來會像這樣
<p>Hello, <action-text-attachment sgid="BAh7CEkiCG…"><span><img src="..."> Jane Doe</span></action-text-attachment>.</p>
6.4 為 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>
6.5 為未解析的實例或遺失的 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>
6.6 透過 API 可附加
如果您的架構不遵循傳統的 Rails 伺服器端呈現模式,那麼您可能會發現自己有一個後端 API(例如,使用 JSON),該 API 將需要一個單獨的端點來上傳檔案。該端點將需要建立一個 ActiveStorage::Blob
並傳回其 attachable_sgid
{
"attachable_sgid": "BAh7CEkiCG…"
}
之後,您可以使用 attachable_sgid
並使用 <action-text-attachment>
標籤將其插入前端程式碼中的富文本內容中
<action-text-attachment sgid="BAh7CEkiCG…"></action-text-attachment>
7 其他
7.1 避免 N+1 查詢
如果您希望預先載入相依的 ActionText::RichText
模型,假設您的富文本欄位名稱為 content
,則可以使用具名的範圍
Article.all.with_rich_text_content # Preload the body without attachments.
Article.all.with_rich_text_content_and_embeds # Preload both body and attachments.