PhoenixDatastar.Socket (PhoenixDatastar v0.1.12)

Copy Markdown View Source

Socket struct for PhoenixDatastar, similar to Phoenix.LiveView.Socket. Holds the view module, session id, assigns, signals, private data, and queued events.

Assigns vs Signals

  • Assigns are server-side state, never sent to the client. Use assign/2,3 and update/3 to work with assigns. They are available in templates as @key.

  • Signals are Datastar reactive state sent to the client via SSE. Use put_signal/2,3 and update_signal/3 to work with signals. They must be JSON-serializable. The client accesses them via Datastar expressions like $count. Signals are not available as @key in templates — Datastar handles their rendering client-side.

Summary

Functions

Merges assigns into the socket from a map or keyword list.

Assigns a single key-value pair to the socket's server-side assigns.

Queues a console.log to be executed on the client via SSE.

Queues a JavaScript script to be executed on the client via SSE.

Strips Phoenix LiveView debug annotations from HTML if configured.

Creates a new socket with standard assigns.

Queues an HTML patch to be sent via SSE.

Merges signals into the socket from a map or keyword list.

Puts a single Datastar signal on the socket.

Queues a redirect to be executed on the client via SSE.

Updates an assign using a function.

Updates a signal using a function.

Types

event()

@type event() :: {:patch, String.t(), String.t()} | {:script, String.t(), keyword()}

t()

@type t() :: %PhoenixDatastar.Socket{
  assigns: map(),
  events: [event()],
  id: String.t() | nil,
  private: map(),
  signals: map(),
  view: module()
}

Functions

assign(socket, new_assigns)

@spec assign(t(), map() | keyword()) :: t()

Merges assigns into the socket from a map or keyword list.

Examples

assign(socket, %{user: user, settings: settings})
assign(socket, user: user, settings: settings)

assign(socket, key, value)

@spec assign(t(), atom(), any()) :: t()

Assigns a single key-value pair to the socket's server-side assigns.

Assigns are never sent to the client. Use put_signal/3 for client-side Datastar signals.

Examples

assign(socket, :user, %User{name: "Alice"})

console_log(socket, message, opts \\ [])

@spec console_log(t(), term(), keyword()) :: t()

Queues a console.log to be executed on the client via SSE.

Options

  • :level - Console method: :log, :warn, :error, :info, :debug (default: :log)
  • Plus all options from execute_script/3

Examples

socket
|> console_log("Debug message")

socket
|> console_log("Warning!", level: :warn)

socket
|> console_log(%{user: "alice", action: "login"}, level: :info)

execute_script(socket, script, opts \\ [])

@spec execute_script(t(), String.t(), keyword()) :: t()

Queues a JavaScript script to be executed on the client via SSE.

Options

  • :auto_remove - Remove script tag after execution (default: true)
  • :attributes - Map of additional script tag attributes

Examples

socket
|> execute_script("alert('Hello!')")

socket
|> execute_script("console.log('debug')", auto_remove: false)

# ES module script
socket
|> execute_script("import {...} from 'module'", attributes: %{type: "module"})

maybe_strip_debug_annotations(html)

@spec maybe_strip_debug_annotations(String.t()) :: String.t()

Strips Phoenix LiveView debug annotations from HTML if configured.

When config :phoenix_datastar, :strip_debug_annotations, true is set (typically in dev.exs), this removes:

  • HTML comments added by debug_heex_annotations (e.g., <!-- @caller ... -->)
  • data-phx-loc attributes added by debug_attributes

This allows PhoenixDatastar SSE patches to work correctly even when LiveView debug annotations are enabled in development. The initial page load will still have annotations for debugging, but SSE patches will be clean.

new(session_id, view, base_path, opts \\ [])

@spec new(String.t(), module(), String.t(), keyword()) :: t()

Creates a new socket with standard assigns.

Options

  • :live - Whether this is a live (stateful) view. Defaults to true. When false, stream_path is set to nil.
  • :flash - Flash map. Defaults to %{}.

Examples

Socket.new(session_id, MyApp.CounterStar, "/counter")
Socket.new(session_id, MyApp.PageStar, "/about", live: false)

patch_elements(socket, selector, render_fn)

@spec patch_elements(
  t(),
  String.t(),
  (map() -> Phoenix.HTML.Safe.t()) | Phoenix.HTML.Safe.t()
) :: t()

Queues an HTML patch to be sent via SSE.

The selector is a CSS selector targeting the element to patch. The second argument can be either:

  • A render function that takes assigns and returns HTML
  • Raw HTML content (must implement Phoenix.HTML.Safe)

The render function receives socket.assigns (server-side state only, not signals).

Examples

With a render function (recommended for pipelines):

socket
|> assign(:items, updated_items)
|> patch_elements("#items", &render_items/1)
|> then(&{:noreply, &1})

defp render_items(assigns) do
  ~H|<ul id="items"><li :for={item <- @items}>{item}</li></ul>|
end

With raw HTML:

socket
|> patch_elements("#count", ~H"<span id="count">42</span>")

put_signal(socket, new_signals)

@spec put_signal(t(), map() | keyword()) :: t()

Merges signals into the socket from a map or keyword list.

Examples

put_signal(socket, %{count: 0, name: "test"})
put_signal(socket, count: 0, name: "test")

put_signal(socket, key, value)

@spec put_signal(t(), atom(), any()) :: t()

Puts a single Datastar signal on the socket.

Signals are sent to the client and must be JSON-serializable. They are accessed client-side via Datastar expressions (e.g., $count).

Examples

put_signal(socket, :count, 0)

redirect(socket, url, opts \\ [])

@spec redirect(t(), String.t(), keyword()) :: t()

Queues a redirect to be executed on the client via SSE.

Uses setTimeout for proper browser history handling, especially in Firefox.

Options

Same as execute_script/3.

Examples

socket
|> redirect("/dashboard")

socket
|> redirect("https://example.com")

update(socket, key, fun)

@spec update(t(), atom(), (any() -> any())) :: t()

Updates an assign using a function.

Examples

update(socket, :user, &User.increment_visits/1)

update_signal(socket, key, fun)

@spec update_signal(t(), atom(), (any() -> any())) :: t()

Updates a signal using a function.

Examples

update_signal(socket, :count, &(&1 + 1))