Packages
live_svelte
0.1.0-rc3
0.18.0
0.18.0-rc0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.15.0-rc.6
0.15.0-rc.5
0.15.0-rc.4
0.15.0-rc.3
0.15.0-rc.2
0.15.0-rc.1
0.14.1
0.14.0
0.13.3
0.13.3-rc.1
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.9.0-rc2
0.9.0-rc1
0.9.0-rc0
0.8.0
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.5.0-rc0
0.4.2
0.4.2-rc0
0.4.1
0.4.1-rc0
0.4.0
0.3.5
0.3.5-rc1
0.3.5-rc0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.3.0-rc1
0.3.0-rc0
0.2.0
0.2.0-rc1
0.2.0-rc0
0.1.0
0.1.0-rc5
0.1.0-rc4
0.1.0-rc3
0.1.0-rc2
0.1.0-rc1
0.1.0-rc0
E2E reactivity for Svelte and LiveView
Current section
Files
Jump to
Current section
Files
lib/live_component.ex
defmodule LiveSvelte do
@moduledoc ~S'''
## Examples
### LiveView
defmodule App.SvelteLive do
use App, :live_view
def render(assigns) do
~H"""
<.live_component
module={LiveSvelte}
id="Example"
name="Example"
props={%{items: @items, i: @i, showItems: @show_items}}
/>
"""
end
def handle_event("add_item", %{"name" => name}, socket) do
socket =
socket
|> assign(:items, socket.assigns.items ++ [name])
|> assign(:i, length(socket.assigns.items) + 1)
|> assign(:show_items, true)
{:noreply, socket}
end
def mount(_params, _session, socket) do
{:ok,
socket
|> assign(:items, ["One", "Two", "Three", "Four", "Five"])
|> assign(:i, 3)
|> assign(:show_items, true)}
end
end
### Svelte Component
<script>
import {slide, fly} from 'svelte/transition'
export let pushEvent
export let i = 5
export let items = []
export let showItems = true
let newItemName
function addItem() {
pushEvent('add_item', {name: newItemName})
newItemName = ''
}
</script>
<div class="flex flex-col">
<label>
<input type="checkbox" bind:checked={showItems}>
show list
</label>
<label>
<input type="range" bind:value={i} max={items.length}>
</label>
<div class="mb-2">
<input type="test" bind:value={newItemName} class="border rounded px-2 py-1"/>
<button on:click={addItem} class="bg-black rounded text-white px-2 py-1">Add item</button>
</div>
</div>
{#if showItems}
<div transition:fly={{x: -20}}>
{#each items.slice(0, i) as item}
<div transition:slide|local class="py-2 border-t border-[#eee]">
{item}
</div>
{/each}
</div>
{/if}
'''
use Phoenix.LiveComponent
import Phoenix.HTML
attr(:props, :map, default: %{})
attr(:name, :string)
attr(:rendered, :boolean, default: false)
def render(assigns) do
~H"""
<div>
<%!-- TODO: This can return things like <title> which should be in the head --%>
<%!-- <script><%= raw(@ssr_render["head"]) %></script> --%>
<style><%= raw(@ssr_render["css"]["code"]) %></style>
<div
id={id(@name)}
data-name={@name}
data-props={json(@props)}
phx-update="ignore"
phx-hook="SvelteComponent"
>
<%= raw(@ssr_render["html"]) %>
</div>
</div>
"""
end
def update(assigns, socket) do
# Making sure we only render once
ssr_code =
unless connected?(socket) do
ssr_render(assigns.name, assigns[:props])
end
socket =
socket
|> assign(assigns)
|> assign(:ssr_render, ssr_code)
{:ok, socket}
end
def ssr_render(name, props \\ %{}) do
NodeJS.call!({"svelte/render", "render"}, [name, props])
end
defp json(props) do
props
|> Jason.encode()
|> case do
{:ok, encoded} -> encoded
{:error, _} -> ""
end
end
defp id(name), do: "#{name}-#{System.unique_integer([:positive])}"
end