Component 除了在同一個 module 用之外也能拉出來放
我們來把 logo 拉出來到 lib/card_web/component.ex
defmodule CardWeb.Component do
use Phoenix.Component
def logo(assigns) do
~H"""
<header class="text-blue-500 m-4">
<a href="/" class="text-5xl font-serif">
? CardyTotala
</a>
</header>
"""
end
end
我要用到的時候我可以用
<CardWeb.Component.logo />
如果我有在要用的地方 import CardWeb.Component 的話,
我就可以直接使用
<.logo />
def ready(assigns) do
~H"""
<div class="flex flex-col p-4 text-center mt-8">
<div class="bg-blue-300 w-32 h-8 rounded-xl transform skew-x-12 -rotate-6 translate-y-6 translate-x-20"></div>
<h2 class="transform text-2xl font-serif">Room status</h2>
<div class="flex justify-between mt-2 w-60">
<.ready_status player="Host" ready={@room.host_ready}/>
<.ready_status player="Guest" ready={@room.guest_ready}/>
</div>
<%= unless Map.get(@room, :"#{@player}_ready") do %>
<div class="flex justify-between w-40 mx-auto mt-8">
<div class="text-lg text-center">Are you ready?</div>
<div class="h-8">
<div class="bg-green-300 w-8 h-8 rounded-xl transform skew-x-12 -rotate-45 translate-x-2"></div>
<button class="transform -translate-y-7 text-xl">Yes</button>
</div>
</div>
<% end %>
</div>
"""
end
def ready_status(assigns) do
~H"""
<div class="flex">
<%= @player %>:
<%= if @ready do %>
<div class={"ml-8 bg-green-300 w-6 h-6 rounded-3xl transform -skew-x-3 rotate-12"}></div>
<% else %>
<div class={"ml-8 bg-red-300 w-6 h-6 rounded-3xl transform -skew-x-3 rotate-12"}></div>
<% end %>
</div>
"""
end
畫面在 host 與 guest 的狀態
在玩家按下 準備好 按鈕的時候,這次不只要更新畫面上的狀態,還要跟對手同步
要做的事情有
感覺要包一些進 room.ex
defmodule Card.Room do
defstruct id: nil, game: nil, game_pid: nil, host_ready: false, guest_ready: false
# 訂閱某個 room_id 頻道
def subscribe(id) do
Phoenix.PubSub.subscribe(Card.PubSub, id)
end
# 廣播 room 物件至某個 room_id
def broadcast(id, room) do
Phoenix.PubSub.broadcast(Card.PubSub, id, room)
end
# 從 ETS 表裡面用 room_id 找 room ,沒找到就回 nil
def get(id) do
case :ets.lookup(:rooms, id) do
[{_, room}] -> room
_ -> nil
end
end
# 在 ETS 表上更新指定 room ,順便廣播
def update(id, room) do
:ets.insert(:rooms, {id, room})
broadcast(room.id, room)
end
end
這樣子 mount 那邊要拿 room 的時後也要記得改用 get
把上面那些方法寫好應該就很快了
phx-click
<button phx-click="ready" class="transform -translate-y-7 text-xl">Yes</button>
接收 ready 事件
def handle_event("ready", _params, %{assigns: %{player: player, room: room}} = socket) do
room =
socket.assigns.room
|> Map.replace(:"#{player}_ready", true)
# 2. 儲存更新後的數值
Room.update(room.id, room)
{:noreply, socket}
end
收到新的 room 就更新就好了
def handle_info(room, socket) do
{:noreply, assign(socket, :room, room)}
end
LiveView 這樣統一狀態超讚的,畫面自己就會跟著變了
動圖: