-
-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Passing inner block content using LiveSvelte.Components macro does not work #101
Comments
We have to be careful with slots as they're not working great and are an experimental feature. Svelte 5 might fix it though as slots will be passed as props, and as such it would present us with a clear API for slots defined from the main component. Currently slots are a bit hacky. That being said, probably a good idea to make slots work across the board with the current version of Svelte. Besides this, no further insight. Feel free to create a PR if you have a fix! |
Yeah I followed so many issues that have been raised against svelte over the years in respect of passing slots and events to dynamic components (many from eco-systems wanting to support svelte) but can't due to the limitations of component initialization. It is definitely a sore point, and I am amazed it hasn't been taken seriously. I did find a supported svelte wrapper svelte-retag which provides a web component wrapper for svelte 3 and 4 and deals with some of the slot issues you have in #52. It might just be the answer to better slot handling in the interim. I note they add support for dynamically creating slots and address many corner cases with compos-ability and rendering of custom components. I do have a couple of modifications to components.ex and I will work up a PR for addressing this issue (once I get a few other things sorted). I will also raise a PR for handling component generation across nested directories. |
@woutdp To follow up on this I have successfully coerced the props and slots for the component helper. However whilst I was working through this I did also notice with any kind of nesting (using component helper or not), that the For example, using the following markup with 4 levels using the previous HelloWorld component: <.HelloWorld>
<.HelloWorld>
<.HelloWorld>
<.HelloWorld />
</.HelloWorld>
</.HelloWorld>
</.HelloWorld> or similarly the non-component way: <.svelte name="HelloWorld">
<.svelte name="HelloWorld">
<.svelte name="HelloWorld">
<.svelte name="HelloWorld" />
</.svelte>
</.svelte>
</.svelte> The assigns within Innermost (level 4) "HelloWorld": %{
init: true,
name: "HelloWorld",
socket: nil,
slots: %{},
props: %{},
inner_block: [],
__changed__: nil,
__given__: %{name: "HelloWorld", __changed__: nil},
class: nil,
ssr: true,
live_json_props: %{},
ssr_render: %{
"css" => %{"code" => "", "map" => nil},
"head" => "",
"html" => "Hello World\n"
}
} Level 3 "HelloWorld": %{
init: true,
name: "HelloWorld",
socket: nil,
slots: %{
default: "<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>"
},
props: %{},
__changed__: nil,
__given__: %{
name: "HelloWorld",
inner_block: [
%{
inner_block: #Function<10.22662313/2 in MyAppWeb.PageHTML.home/1>,
__slot__: :inner_block
}
],
__changed__: nil
},
class: nil,
ssr: true,
live_json_props: %{},
ssr_render: %{
"css" => %{"code" => "", "map" => nil},
"head" => "",
"html" => "Hello World\n<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>"
}
} The level 2 "HelloWorld": %{
init: true,
name: "HelloWorld",
socket: nil,
slots: %{
default: "<script></script>\n <div id=\"HelloWorld-6915\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY3ODciIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ie30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAoKICA8L2Rpdj4="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>\n </div>"
},
props: %{},
__changed__: nil,
__given__: %{
name: "HelloWorld",
inner_block: [
%{
inner_block: #Function<8.22662313/2 in MyAppWeb.PageHTML.home/1>,
__slot__: :inner_block
}
],
__changed__: nil
},
class: nil,
ssr: true,
live_json_props: %{},
ssr_render: %{
"css" => %{"code" => "", "map" => nil},
"head" => "",
"html" => "Hello World\n<script></script>\n <div id=\"HelloWorld-6915\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY3ODciIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ie30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAoKICA8L2Rpdj4="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>\n </div>"
}
} The top Level 1 "HelloWorld": %{
init: true,
name: "HelloWorld",
socket: nil,
slots: %{
default: "<script></script>\n <div id=\"HelloWorld-7043\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY5MTUiIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ieyZxdW90O2RlZmF1bHQmcXVvdDs6JnF1b3Q7UEhOamNtbHdkRDQ4TDNOamNtbHdkRDRLSUNBOFpHbDJJR2xrUFNKSVpXeHNiMWR2Y214a0xUWTNPRGNpSUdSaGRHRXRibUZ0WlQwaVNHVnNiRzlYYjNKc1pDSWdaR0YwWVMxd2NtOXdjejBpZTMwaUlHUmhkR0V0YzNOeUlHUmhkR0V0YkdsMlpTMXFjMjl1UFNKN2ZTSWdaR0YwWVMxemJHOTBjejBpZTMwaUlIQm9lQzExY0dSaGRHVTlJbWxuYm05eVpTSWdjR2g0TFdodmIyczlJbE4yWld4MFpVaHZiMnNpSUdOc1lYTnpQU0lpUGdvZ0lDQWdQSE4wZVd4bFBqd3ZjM1I1YkdVK0NpQWdJQ0JJWld4c2J5QlhiM0pzWkFvS0lDQThMMlJwZGo0PSZxdW90O30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAo8c2NyaXB0Pjwvc2NyaXB0PgogIDxkaXYgaWQ9IkhlbGxvV29ybGQtNjc4NyIgZGF0YS1uYW1lPSJIZWxsb1dvcmxkIiBkYXRhLXByb3BzPSJ7fSIgZGF0YS1zc3IgZGF0YS1saXZlLWpzb249Int9IiBkYXRhLXNsb3RzPSJ7fSIgcGh4LXVwZGF0ZT0iaWdub3JlIiBwaHgtaG9vaz0iU3ZlbHRlSG9vayIgY2xhc3M9IiI+CiAgICA8c3R5bGU+PC9zdHlsZT4KICAgIEhlbGxvIFdvcmxkCgogIDwvZGl2PgogIDwvZGl2Pg=="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6915\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY3ODciIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ie30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAoKICA8L2Rpdj4="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>\n </div>\n </div>"
},
props: %{},
__changed__: nil,
__given__: %{
name: "HelloWorld",
inner_block: [
%{
inner_block: #Function<6.22662313/2 in MyAppWeb.PageHTML.home/1>,
__slot__: :inner_block
}
],
__changed__: nil
},
class: nil,
ssr: true,
live_json_props: %{},
ssr_render: %{
"css" => %{"code" => "", "map" => nil},
"head" => "",
"html" => "Hello World\n<script></script>\n <div id=\"HelloWorld-7043\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY5MTUiIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ieyZxdW90O2RlZmF1bHQmcXVvdDs6JnF1b3Q7UEhOamNtbHdkRDQ4TDNOamNtbHdkRDRLSUNBOFpHbDJJR2xrUFNKSVpXeHNiMWR2Y214a0xUWTNPRGNpSUdSaGRHRXRibUZ0WlQwaVNHVnNiRzlYYjNKc1pDSWdaR0YwWVMxd2NtOXdjejBpZTMwaUlHUmhkR0V0YzNOeUlHUmhkR0V0YkdsMlpTMXFjMjl1UFNKN2ZTSWdaR0YwWVMxemJHOTBjejBpZTMwaUlIQm9lQzExY0dSaGRHVTlJbWxuYm05eVpTSWdjR2g0TFdodmIyczlJbE4yWld4MFpVaHZiMnNpSUdOc1lYTnpQU0lpUGdvZ0lDQWdQSE4wZVd4bFBqd3ZjM1I1YkdVK0NpQWdJQ0JJWld4c2J5QlhiM0pzWkFvS0lDQThMMlJwZGo0PSZxdW90O30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAo8c2NyaXB0Pjwvc2NyaXB0PgogIDxkaXYgaWQ9IkhlbGxvV29ybGQtNjc4NyIgZGF0YS1uYW1lPSJIZWxsb1dvcmxkIiBkYXRhLXByb3BzPSJ7fSIgZGF0YS1zc3IgZGF0YS1saXZlLWpzb249Int9IiBkYXRhLXNsb3RzPSJ7fSIgcGh4LXVwZGF0ZT0iaWdub3JlIiBwaHgtaG9vaz0iU3ZlbHRlSG9vayIgY2xhc3M9IiI+CiAgICA8c3R5bGU+PC9zdHlsZT4KICAgIEhlbGxvIFdvcmxkCgogIDwvZGl2PgogIDwvZGl2Pg=="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6915\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{"default":"PHNjcmlwdD48L3NjcmlwdD4KICA8ZGl2IGlkPSJIZWxsb1dvcmxkLTY3ODciIGRhdGEtbmFtZT0iSGVsbG9Xb3JsZCIgZGF0YS1wcm9wcz0ie30iIGRhdGEtc3NyIGRhdGEtbGl2ZS1qc29uPSJ7fSIgZGF0YS1zbG90cz0ie30iIHBoeC11cGRhdGU9Imlnbm9yZSIgcGh4LWhvb2s9IlN2ZWx0ZUhvb2siIGNsYXNzPSIiPgogICAgPHN0eWxlPjwvc3R5bGU+CiAgICBIZWxsbyBXb3JsZAoKICA8L2Rpdj4="}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n<script></script>\n <div id=\"HelloWorld-6787\" data-name=\"HelloWorld\" data-props=\"{}\" data-ssr data-live-json=\"{}\" data-slots=\"{}\" phx-update=\"ignore\" phx-hook=\"SvelteHook\" class=\"\">\n <style></style>\n Hello World\n\n </div>\n </div>\n </div>"
}
} Is there something we can do in the rendering strategy to avoid this exponential growth? |
Oh that is interesting, I didn't think of this issue. Not seeing a way around it though with how data is being passed to Svelte at the moment. Maybe in a better scenario we wouldn't pass the data through an html attribute, maybe it's possible to do it with straight JS, that way at least the HTML wouldn't grow exponentially. Just thinking out loud here |
I was looking at a few options including live-state and live-data for transporting state. Then I began investigating use of web components which I actually think might be the answer for wrapping Svelte components using Lit and liveview support for working with web components using live_elements. Lit also supports SSR and client side hydration and slot binding is possible with Declarative Shadow DOM. It is likely I will move forward with using either Shoelace web components but more probably the new Adobe Spectrum 2 web components as they have built all their apps on web components, so it should be a fairly solid design language, fully functional, well documented and tested |
Expected Outcome
Use component helpers with inner blocks, just as we can with
<.svelte .../>
.Issue
I added
use LiveSvelte.Components
tohtml_helpers
inlib/myapp_web.ex
:The generated component function fails to render the inner block when passing slot content to the
LiveSvelte.svelte
component, as it is not picked up inLiveSvelte.Slots.filter_slots_from_assigns/1
.I did some investigation and inspected the assigns:
With a test component
HelloWorld.svelte
:Rendering with
<.HelloWorld />
in our markup we get the following assigns:Rendering with
<.svelte name="HelloWorld">Foo</.svelte>
with a block we get working slot/assigns:Rendering with
<.HelloWorld>Foo</.HelloWorld>
the generated function component we get broken assigns with the slot content nested onprops
:I also discovered that Phoenix actually doesn't let one dynamically create components with attrs or slots, raising an error when attempting to add an inner_block slot on the component generator:
What can we do?
It seems the obvious way forward is for
LiveSvelte.Slots
to also detect slots on theprops
assign in its filtering.Did you have any further insights? Is there a better way to dynamically create the component functions that is more aligned with how phoenix components work?
The text was updated successfully, but these errors were encountered: