iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

速成 Phoenix, 2022年最受喜愛框架系列 第 27

{27, LiveView, "聊天室介面"}

  • 分享至 

  • xImage
  •  

LiveView 的優勢

朋友問,LiveView 把所有的東西都傳到後端才反應,這樣一來一回會不會太慢,
我的回答是看情況,
如果我只是要簡單的打開關閉選單,那我把選單的開關狀態傳到伺服器,再反映到畫面上,還滿浪費的,
對於這種情境 LiveView 提供了 Phoenix.LiveView.JS 模組,裡面有客戶端的特別方法可以在 phx-click 上呼叫,當然,如果需要更複雜的 Javascript 功能或是使用其他 Javascript 套件,隨時都可以用 hook 把他勾進來。
但實際上大部分的情況都是需要伺服器來處理的,好吧除非你做 Javascript 單機遊戲,
用 Javascript 框架也常常做什麼都要跟後端確認。
因為 LiveView 每個客戶端的狀態是在伺服器端,有很多東西要呼叫後端非常好處理,不需要新增一個功能就需要前後端各建一個 API 來溝通。

客戶端的狀態是在伺服器端其中一個優勢就是多人互動變得很方便

聊天室介面

我們下一篇要使用 Channel 來做多人聊天室,我們先把個人版本做好

context

這邊我們可以偷懶一下用產生器來產生訊息的 context

mix phx.gen.context Messages Message messages content author

等等 後面那些是誰
Phoenix 的產生器都很貼心的有說明,我們輸入

mix phx.gen.context

他就會顯示出範例

在我們的的指令裡
mix phx.gen.context Messages Message messages content author
第一個 Messages 是 context 名稱
第二個 Message 是 schema 名稱
第三個 messages 是資料庫表格名稱
其他的是裡面的欄位, context:string 如果是 string 可以省略不打

好了之後我們來修改一下 Messages context
lib/blog/messages.ex

用 context 產生器會幫我們做好 CURD 等相關方法
這邊我們要修改一下 list_messages 方法

為了要讓畫面不要太複雜,
我們幫他加上倒著取的順序條件

  def list_messages do
    Message
    |> order_by(desc: :id)
    |> Repo.all()
  end

記得 mix ecto.migrate

還是從 Router 開始

live("/chat_room", ChatRoomLive, :chat_room)

ChatRoomLive

簡單做,這個就跟 CounterLive 一樣放在 live 資料夾就行了
lib/blog_web/live/chat_room_live.ex

defmodule BlogWeb.ChatRoomLive do
  # 帶入 LiveView 要用的東西
  use BlogWeb, :live_view
  # alias 剛剛產生的 Context
  alias Blog.Messages

  # LiveView 從這邊開始
  def mount(_params, _session, socket) do
    {:ok,
     # 我們這次放盡 assigns 的東西有:
     assign(socket, %{
       # 新訊息用的 changeset
       changeset: Messages.change_message(%Messages.Message{}),

       # 讀取目前的訊息
       messages: Messages.list_messages(),

       # 名字我們些寫死
       author: "阿強",

       # 強迫 LiveView 更新表單用的序號,算是一個 hack,後面補充
       message_sid: 0
     })}
  end

  def handle_event("new_message", %{"message" => message_attrs}, socket) do
    # 儲存訊息
    case Messages.create_message(message_attrs) do
      # 成功的話
      {:ok, _message} ->
        {:noreply,
         assign(socket, %{
           # 更新畫面上的 messages
           messages: Messages.list_messages(),

           # 更新表格上面的 sid 讓表格重畫
           message_sid: socket.assigns.message_sid + 1
         })}

      {:error, changeset} ->
        {:noreply, assign(socket, %{changeset: changeset})}
    end
  end

  def render(assigns) do
    ~H"""
    <.message_form changeset={@changeset} author={@author} message_sid={@message_sid} />
    <.messages messages={@messages} />
    """
  end

  def messages(assigns) do
    ~H"""
    <div>
      <%= for message <- @messages do %>
        <div><%= "#{message.author}: #{message.content}" %></div>
      <% end %>
    </div>
    """
  end

  def message_form(assigns) do
    ~H"""
    <.form let={form} for={@changeset} phx-submit="new_message" sid={@message_sid}>
      <%= text_input(form, :content, autofocus: true) %>
      <%= hidden_input(form, :author, value: @author) %>
    </.form>
    """
  end
end

這裡有點像是之前做文章列表的簡化版,
因為表格只需要新增,也直接在同個 LiveView 上直接做就行了

但要注意的是如果沒有放隨著送出訊息改變的 sid,因為一直在同一頁,所以送出表格後,LiveView 不會更新我們的表格,畢竟 @changeset 沒有改變,會導致輸入欄在訊息送出後不會清空。
這邊有幾個解法,但最簡單暴力的方式就是放一個會變動的值讓他更新。
(在這次我們每送出一次訊息 sid 就 + 1)

這樣就完成我們的孤僻聊天室
目前只能跟自己聊天

https://ithelp.ithome.com.tw/upload/images/20221001/20141054mtcAkXyo0K.png


上一篇
{26, LiveView, "共用 Component"}
下一篇
{28, PubSub, "即時多人用PubSub"}
系列文
速成 Phoenix, 2022年最受喜愛框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言