Skip to content

Commit d95af53

Browse files
docs: document hooks and JS commands in dead views (#4246)
* docs: document hooks and JS commands in dead views * docs: address review feedback on regular view section * Update guides/client/js-interop.md --------- Co-authored-by: Steffen Deusch <steffen@deusch.me>
1 parent 96f221b commit d95af53

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

guides/client/bindings.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ If `phx-mounted` is used on the initial page render, it will run at the earliest
351351
opportunity. For elements outside of a LiveView, this is as soon as `liveSocket.connect()`
352352
is executed. For elements inside of a LiveView, this is only after the initial socket
353353
connection is established and the LiveView is mounted.
354+
See [Hooks and JS commands outside of a LiveView](js-interop.md#hooks-and-js-commands-outside-of-a-liveview).
354355

355356
To react to elements being removed from the DOM, the `phx-remove` binding
356357
may be specified, which can contain a `Phoenix.LiveView.JS` command to execute.
@@ -376,6 +377,7 @@ recovers:
376377

377378
`phx-connected` and `phx-disconnected` are only executed when operating
378379
inside a LiveView container. For static templates, they will have no effect.
380+
See [Hooks and JS commands outside of a LiveView](js-interop.md#hooks-and-js-commands-outside-of-a-liveview).
379381

380382
## LiveView events prefix
381383

guides/client/js-interop.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,11 @@ or removed by the server, a hook object may be provided via `phx-hook`.
159159
* `disconnected` - the element's parent LiveView has disconnected from the server
160160
* `reconnected` - the element's parent LiveView has reconnected to the server
161161

162-
*Note:* When using hooks outside the context of a LiveView, `mounted` is the only
163-
callback invoked, and only those elements on the page at DOM ready will be tracked.
164-
For dynamic tracking of the DOM as elements are added, removed, and updated, a LiveView
165-
should be used.
162+
*Note:* hooks also run on regular pages that are *not* LiveViews — see
163+
[Hooks and JS commands outside of a LiveView](#hooks-and-js-commands-outside-of-a-liveview).
164+
In that case, `mounted` is the only callback invoked, and only those elements on the page
165+
at DOM ready will be tracked. For dynamic tracking of the DOM as elements are added,
166+
removed, and updated, a LiveView should be used.
166167

167168
The above life-cycle callbacks have in-scope access to the following attributes:
168169

@@ -498,3 +499,48 @@ Hooks.MyHook = {
498499
Setting IDs directly via `node.id = "..."` or other direct DOM manipulation methods will cause DOM patching issues. Always use `js().setAttribute()` instead.
499500

500501
If the server has already assigned an ID to an element, you cannot replace it with a different ID from the client side. Client-side IDs should only be set on elements that have no server-assigned ID.
502+
503+
## Hooks and JS commands outside of a LiveView
504+
505+
Hooks (`phx-hook`) and `Phoenix.LiveView.JS` commands are not exclusive to LiveViews.
506+
They also work on regular pages [rendered by a normal Phoenix
507+
controller](https://hexdocs.pm/phoenix/controllers.html#rendering) — pages with no
508+
live connection (sometimes referred to as *dead views*, *dead renders*,
509+
*static pages*, or simply markup *outside of a LiveView*).
510+
511+
This includes markup that lives *outside* the live container on a page that does have a
512+
LiveView, such as your root layout — those elements belong to a body-level regular view,
513+
not to the LiveView.
514+
515+
To enable it, the page must load and connect `LiveSocket`, exactly as a LiveView page does:
516+
517+
```javascript
518+
import {LiveSocket} from "phoenix_live_view"
519+
520+
const liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks})
521+
liveSocket.connect()
522+
```
523+
524+
### What works in a regular view
525+
526+
* **`phx-hook`** — the `mounted` callback runs, and only for elements present at DOM
527+
ready. The other callbacks (`updated`, `beforeUpdate`, `destroyed`, `disconnected`,
528+
`reconnected`) are never invoked, because a regular view receives no updates from a server.
529+
* **`phx-mounted`** — runs once the document is ready (`DOMContentLoaded`) and
530+
`liveSocket.connect()` has been called (see [Bindings](bindings.md#dom-patching)).
531+
* **`phx-click`** and other event bindings that trigger **purely client-side `JS`
532+
commands** — for example `JS.toggle/1`, `JS.show/1`, `JS.hide/1`, `JS.add_class/1`,
533+
`JS.dispatch/1`, and `JS.transition/1`. These execute entirely in the browser, so they
534+
work with no server round-trip.
535+
* **`JS.navigate/1`** and **`JS.patch/1`** — if the page has a connected LiveView (for
536+
example a link in the root layout that sits outside the live container), they perform
537+
normal live navigation against it. On a fully static page with no LiveView, they
538+
gracefully fall back to a full-page browser navigation to the target URL.
539+
540+
### What does not work in a regular view
541+
542+
* Anything that needs a connected LiveView: `JS.push/1`, form bindings (`phx-change`,
543+
`phx-submit`), and other event bindings that push to the server have no LiveView
544+
process to reach, so they have no effect.
545+
* The `phx-connected` and `phx-disconnected` bindings — they only take effect inside a
546+
LiveView container and have no effect on a regular view.

0 commit comments

Comments
 (0)