Skip to content

Instantly share code, notes, and snippets.

@Valian
Created May 2, 2024 17:34
Show Gist options
  • Save Valian/6418d0d4af528d8a15963e27100431f5 to your computer and use it in GitHub Desktop.
Save Valian/6418d0d4af528d8a15963e27100431f5 to your computer and use it in GitHub Desktop.
Phoenix LiveView teleport hook
<.flash_group flash={@flash} />
<main>
<%= @inner_content %>
</main>
<%!-- We're doing teleport because navigation is rendered in root layout which is not updated --%>
<%!-- Notice "hidden" class, it's important because we're not really teleporting content, but duplicating it --%>
<div :if={@current_user} id="onboarding-render" phx-hook="teleport" data-teleport-target="onboarding" class="hidden">
<MyApp.Onboarding.onboarding current_user={@current_user} />
</div>
import { teleportHook } from "./hooks/teleport";
// ...
const liveSocket = new LiveSocket("/live", Socket, {
params: { _csrf_token: csrfToken },
hooks: { teleport: teleportHook },
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<nav>
<div id="onboarding">
<%!-- Teleported content will land here --%>
</div>
</nav>
<%= @inner_content %>
</body>
</html>
// Teleport hook for phoenix live view
// It's not a real teleport, but rather a synchronized duplication of DOM Element
// On mount and on each update we copy the DOM element to the destination.
// I tried to really move it to the destination, but it screws up phoenix DOM morph updates
// Example use case - updating onboarding being part of navigation rendered in root.html.heex
// Example use case 2 - updating footer
const getTarget = (el: HTMLElement) => {
const target = el.dataset.teleportTarget
if (!target) {
console.error("No teleport target provided")
return
}
const targetElement = document.getElementById(target)
if (!targetElement) {
console.error("No teleport target element found")
return
}
return targetElement
}
export const teleportHook = {
mounted() {
this.teleport()
},
updated() {
this.teleport()
},
teleport() {
const target = getTarget(this.el)
if(target) {
target.replaceChildren(...this.el.cloneNode(true).childNodes)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment