【问题标题】:How to send events directly from Phoenix Live View to a component如何将事件直接从 Phoenix Live View 发送到组件
【发布时间】:2022-02-09 03:14:46
【问题描述】:

我对 Phoenix LiveView 组件 (v0.15.4) 有疑问。

在文档中,有一个示例代码:

def handle_info({:updated_card, card}, socket) do
  send_update CardComponent, id: card.id, board_id: socket.assigns.id
  {:noreply, socket}
end

LiveView 组件没有handle_info/2 回调,因此调用send_update/3 将事件从父LiveView 重定向到特定组件。

但是,我想将事件直接从父 LiveView 发送到特定组件,因为我想做这样的事情

# parent LiveView
def handle_info({:new_card, card_id}, socket) do
  send_event CardStackComponent, id: :card_stack, event: "add_card", params: %{"card_id" => card_id}
  {:noreply, socket}
end

# component
def handle_event("add_card", %{"card_id" => card_id} = _params, socket) do
  card = get_card(card_id)

  socket =
    update(socket, :cards, fn cards ->)
      [card | cards]     
    end)
    
  {:noreply, socket}
end

当然,没有Phoenix.LiveView.send_event/2这样的功能。以上是一个虚构的代码来说明我的意图。

我知道如何将事件从客户端(浏览器)发送到特定组件,方法是向 HTML 元素添加 phx-target="<%= @myself %>" 属性,或者在 JavaScript 程序中使用 pushEventTo 方法。

但是,据我所知,没有办法将事件直接从父 LiveView 发送到特定组件。

有什么解决方法或更好的方法吗?

【问题讨论】:

  • 我建议坚持将更新传递给组件的标准方式,但是您始终可以使用 PubSub 并在那里传递事件。

标签: elixir phoenix-live-view


【解决方案1】:

Phoenix Pubsub 就是答案。

defmodule QuickPick.Cards.LiveUpdates do
  @topic inspect(__MODULE__)

  @doc "subscribe for all cards"
  def subscribe_live_view do
    Phoenix.PubSub.subscribe(QuickPick.PubSub, topic(), link: true)
  end

  @doc "subscribe for specific card"
  def subscribe_live_view(card_id) do
    Phoenix.PubSub.subscribe(QuickPick.PubSub, topic(card_id), link: true)
  end

  @doc "notify for all cards"
  def notify_live_view(message) do
    Phoenix.PubSub.broadcast(QuickPick.PubSub, topic(), message)
  end

  @doc "notify for specific card"
  def notify_live_view(card_id, message) do
    Phoenix.PubSub.broadcast(QuickPick.PubSub, topic(card_id), message)
  end

  defp topic, do: @topic
  defp topic(card_id), do: topic() <> to_string(card_id)
end

订阅并处理更新

def mount(session, socket) do
  LiveUpdates.subscribe_live_view(card.id)
  ...
end

def handle_info({_requesting_module, [:recommendations, :updated], []}, socket) do
  ...
end

更新

LiveUpdates.notify_live_view(
  card.id,
  {__MODULE__, [:recommendations, :updated], []}
)

【讨论】:

    【解决方案2】:

    你也可以

    # parent LiveView
    def handle_info({:new_card, card_id}, socket) do
      send_update CardStackComponent, id: :card_stack, card_id: card_id
      {:noreply, socket}
    end
    
    #component
    def update(%{card_id: card_id} = _assigns, socket) do
      card = get_card(card_id)
    
      socket =
        update(socket, :cards, fn cards ->
          [card | cards]     
        end)
      {:ok, socket}
    end
    

    您必须记住,每次渲染都会调用update/2。更多信息here

    还值得一提的是,您可能希望在 LiveView 或 LiveComponent 中保持状态,但不能同时在两者中保持状态,如 here 所述

    【讨论】:

      猜你喜欢
      • 2016-07-20
      • 1970-01-01
      • 2023-03-28
      • 2021-09-13
      • 1970-01-01
      • 2021-08-26
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      相关资源
      最近更新 更多