Phoenix LiveView 0.19 已發布
克里斯·麥考德於 2023 年 5 月 29 日張貼
LiveView 0.19.0 已推出!此版本包含期待已久的動態表單功能、新的串流原語,並彌補了您希望 LiveView 能做到的功能差距。這是我們在 LiveView 1.0 之前計畫的最後一個主要版本。
開源 TodoTrek
展示應用程式
如我在 ElixirConfEU 主題演講 中展示和承諾的那樣,我正在開放原始碼 TodoTrek 應用程式,這是一個類似 Trello 的應用程式,展示了新的串流功能,例如拖放、無限捲動、動態表單等。
TodoTrek 實際操作
現在來了解一下功能吧!
增強的串流介面,具有重設和限制
LiveView 0.18 中的串流引入了一種強大的方式,可以在用戶端處理大型集合,而無需將集合儲存在伺服器記憶體中。串流允許開發人員追加、置於前面或任意插入和更新 UI 中的集合中的項目。這為許多類似 SPA 的使用案例開啟了大門,例如即時動態消息和無限饋送,但缺少一些原語。
借助 LiveView 0.19,串流彌補了這些原語的差距。串流可以在 UI 中透過 :limit
選項加以限制。這允許開發人員指示 UI 在將新項目插入串流時保留集合中的前 N 或 N 項。這對於許多情況至關重要,例如防止用戶端因 DOM 中的資料過多而不堪負荷。結合新的視窗繫結,可以毫不費力地實作虛擬化、無限列表,我們稍後將了解到這一點。
除了串流限制外,串流現在還支援 :reset
選項,它在更新串流時清除用戶端上的串流容器。對於任何需要清除結果或重設列表的情況,這都非常有用,例如搜尋自動完成或傳統分頁。
只需使用一個簡單的 stream(socket, :posts, posts, limit: 10)
或 stream(socket, :posts, [], reset: true)
,您現在就可以處理複雜的用戶端集合,而不必考慮它。但這只是可能性的一個開始。
具有拖放插入的巢狀串流
LiveView 0.19 支援巢狀串流,只需要幾行程式碼就能實作拖放功能。想像一個 trello 看板,其中有命名清單,包含代辦事項。你可以拖曳並放置重新排列清單本身,或是清單中的代辦事項。你也可以跨清單拖曳並放置代辦事項。在 LiveView 中,只要在客戶端和伺服器上使用極少的程式碼,就能完成所有這些操作。對於客戶端拖放本身,你可以引入自己的函式庫,並整合 десятки 行程式碼。例如,以下是 TodoTrek
處理代辦事項和清單的拖放
import Sortable from "../vendor/sortable"
Hooks.Sortable = {
mounted(){
let group = this.el.dataset.group
let sorter = new Sortable(this.el, {
group: group ? {name: group, pull: true, put: true} : undefined,
animation: 150,
dragClass: "drag-item",
ghostClass: "drag-ghost",
onEnd: e => {
let params = {old: e.oldIndex, new: e.newIndex, to: e.to.dataset, ...e.item.dataset}
this.pushEventTo(this.el, this.el.dataset["drop"] || "reposition", params)
}
})
}
}
我們在此匯入 sortable.js,然後將其作為 phx-hook
連接。現在,任何串流容器都可以使用下列標記連結有拖放功能的串流
<div id="todos" phx-update="stream" phx-hook="Sortable">
...
</div>
當項目被放置時,LiveView 會收到一個「重新定位」事件,其中包含新的和舊的索引,以及存在的任何資料屬性。
虛擬化無限捲動的視埠繫結
LiveView 0.19 針對處理視埠事件,推出了兩個新的 phx
繫結,分別是 phx-viewport-top
和 phx-viewport-bottom
。當容器的第一個子項到達視窗頂端,或是最後一個子項到達視窗底部時,就會觸發這些事件。結合這兩個事件與串流限制,即可執行「虛擬化」無限捲動,其中只在 DOM 中保留一小部分項目,而使用者則會體驗到包含大量或無限項目清單。在 LiveView 0.19 之前,這種功能需要使用者跳到複雜的 JavaScript 鉤子中,但現在不再需要了。
使用新的 inputs_for
建立動態表單
現在,使用 inputs_for
動態新增和移除輸入,透過勾選插入和移除的核取方塊來支援。Ecto 等函式庫或自訂參數過濾,可以檢查參數並處理新增或移除的欄位。這可以結合 Ecto.Changeset.cast/3
的 :sort_param
和 :drop_param
選項。例如,想像一個父項 Ecto.Schema
,其具有 :emails
has_many
或 embeds_many
關聯。若要從巢狀 inputs_for
轉換使用者輸入,只需設定選項
schema "lists" do
field :title, :string
embeds_many :emails, EmailNotification, on_replace: :delete do
field :email, :string
field :name, :string
end
end
def changeset(list, attrs) do
list
|> cast(attrs, [:title])
|> cast_embed(:emails,
with: &email_changeset/2,
sort_param: :emails_sort,
drop_param: :emails_drop
)
end
我們在此看到 :sort_param
和 :drop_param
選項的實際運用。
注意:使用這些選項時,必須在
has_many
和embeds_many
上使用on_replace: :delete
。
當 Ecto 從表單中看到指定的排序或刪除參數時,它會根據它們在表單中出現的順序對子項進行排序,新增尚未看到的子項,或在刪除參數指示時刪除子項。
此類架構和關聯的標記看起來會像這樣
<.inputs_for :let={ef} field={@form[:emails]}>
<input type="hidden" name="list[emails_sort][]" value={ef.index} />
<.input type="text" field={ef[:email]} placeholder="email" />
<.input type="text" field={ef[:name]} placeholder="name" />
<label>
<input type="checkbox" name="list[emails_drop][]" value={ef.index} class="hidden" />
delete
</label>
</.inputs_for>
<label class="block cursor-pointer">
<input type="checkbox" name="list[emails_sort][]" class="hidden" />
add more
</label>
我們使用 inputs_for
呈現 :emails
關聯性的輸入,每個子代包含電子郵件地址和名稱輸入。在巢狀輸入中,我們呈現隱藏的 list[emails_sort][]
輸入,設定為給定子代的索引。這會告知 Ecto 的強制轉型作業如何對現有子代排序,或要新在哪裡新增子代。接著我們照常呈現電子郵件和名稱輸入。然後我們呈現一個包含「刪除」文字和名稱為 list[emails_drop][]
隱藏核取方塊輸入的標籤,其值包含子代的索引。與之前一樣,這會告知 Ecto 在核取方塊被勾選時刪除該索引下的子代。將核取方塊和文字內容包裹在標籤中,將使得在標籤中所按下的任何內容都能勾選和取消勾選核取方塊。
最後,在 inputs_for
的外部,我們呈現另一個標籤,其中有一個沒有值的 list[emails_sort][]
核取方塊,以及相隨的「新增更多」文字。Ecto 會將未知的排序參數視為新的子代並建構一個新的子代。
這種方法的一大優點是它讓 LiveView 和傳統檢視都能使用相同的表單和強制轉型原件。
縮小想法和實作之間的差距
0.19 中的功能讓你可以輕鬆建置以前只能透過單一頁面應用程式來達成各式各樣的使用案例。想想就像 Slack 一樣具有無限捲動記錄的聊天傳訊,或者像 Twitter 一樣雙向的社群時間線,或者像 Trello 一樣能巢狀拖曳和放下重新排序的待辦事項應用程式。現在你可以建置這些東西,完全不用去思考客戶端必要的細節。這項版本讓我們得以專注在推出 LiveView 1.0。敬請期待!
編碼愉快!
–Chris