-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Option to set slots when create component instance #2588
Comments
workaround with private apis is to do something like this: import { detach, insert, noop } from 'svelte/internal';
function createSlots(slots) {
const svelteSlots = {};
for (const slotName in slots) {
svelteSlots[slotName] = [createSlotFn(slots[slotName])];
}
function createSlotFn(element) {
return function() {
return {
c: noop,
m: function mount(target, anchor) {
insert(target, element, anchor);
},
d: function destroy(detaching) {
if (detaching) {
detach(element);
}
},
l: noop,
};
}
}
return svelteSlots;
}
new Component({
target: element,
props: {
$$slots: createSlots({ slot_name1: element1, slot_name2: element2, ... }),
$$scope: {},
},
}); it seems works for me |
@creaven thanks for this workaround. Saved me a headache converting a v2 modal component that relied on passing in slots to v3! |
@creaven your solution works like a charm! Now the challenge I'm facing is how to pass another component into the slot? For example, to test components that are meant to be used as: <Dropdown>
<DropdownTrigger />
<DropdownList />
</Dropdown> Where the Dropdown component just renders what's passed into the default slot, but then it also creates context, and child components interact with such context, so no possibility to fully test the whole behavior when testing them in isolation. Any suggestions? |
mark. |
also can like this: export function createSlots(slots) {
const svelteSlots = {}
for (const slotName in slots) {
svelteSlots[slotName] = [createSlotFn(slots[slotName])]
}
function createSlotFn([ele, props = {}]) {
if (is_function(ele) && Object.getPrototypeOf(ele) === SvelteComponent) {
const component: any = new ele({})
return function () {
return {
c() {
create_component(component.$$.fragment)
component.$set(props)
},
m(target, anchor) {
mount_component(component, target, anchor, null)
},
d(detaching) {
destroy_component(component, detaching)
},
l: noop,
}
}
}
else {
return function () {
return {
c: noop,
m: function mount(target, anchor) {
insert(target, ele, anchor)
},
d: function destroy(detaching) {
if (detaching) {
detach(ele)
}
},
l: noop,
}
}
}
}
return svelteSlots
} then you can use like this: const { container } = render(Row, {
props: {
gutter: 20,
$$slots: createSlots({ default: [Col, { span: 12 }] }),
$$scope: {},
}
}) it works for me, but i still wait the pr be merged. |
Will this feature be supported? |
Hi guys I am confused. How to give one (or several) components (with props) to another component's slots programatically? I tried @cbbfcd ( #2588 (comment) ) suggestion. import Book from './components/Book.svelte';
import Bubble from './components/Bubble.svelte';
//const root = ...
const slot = [Book, { color: 'green' }];
const bubble = new Bubble({
target: root,
props: {
$$slots: createSlots({ default: slot }),
$$scope: {},
},
}); But it fails and I updated it like so: // from:
if (is_function(ele) && Object.getPrototypeOf(ele) === SvelteComponent) {
// to:
if (is_function(ele) && ele.prototype instanceof SvelteComponent) { the complete code below: import {
create_component,
destroy_component,
detach,
insert,
is_function,
mount_component,
noop,
SvelteComponent
} from 'svelte/internal';
export const createSlots = (slots) => {
const svelteSlots = {}
for (const slotName in slots) {
svelteSlots[slotName] = [createSlotFn(slots[slotName])]
}
function createSlotFn([ele, props = {}]) {
if (is_function(ele) && ele.prototype instanceof SvelteComponent) {
const component: any = new ele({})
return function () {
return {
c() {
create_component(component.$$.fragment)
component.$set(props)
},
m(target, anchor) {
mount_component(component, target, anchor, null)
},
d(detaching) {
destroy_component(component, detaching)
},
l: noop,
}
}
}
else {
return function () {
return {
c: noop,
m: function mount(target, anchor) {
insert(target, ele, anchor)
},
d: function destroy(detaching) {
if (detaching) {
detach(ele)
}
},
l: noop,
}
}
}
}
return svelteSlots
}; However now, it fails like so: The responsible line is: // at createSlotFn (tools.ts:24:30)
const component: any = new ele({}) Any idea what I am doing wrong? |
And just in case it is relevant this is how I setup svelte in webpack. {
test: /\.(html|svelte)$/,
use: {
loader: 'svelte-loader',
options: {
emitCss: true,
preprocess: sveltePreprocess({
postcss: true,
typescript: true,
}),
compilerOptions: {
dev: !env.production,
generate: 'dom',
}
},
},
}, |
@cbbfcd I partially updated you code like this to make it work. import {
destroy_component,
detach,
insert,
is_function,
mount_component,
noop,
SvelteComponent,
} from 'svelte/internal';
export const createSlots = (slots) => {
const svelteSlots = {}
for (const slotName in slots) {
svelteSlots[slotName] = [createSlotFn(slots[slotName])]
}
function createSlotFn([ele, props = {}]) {
if (is_function(ele) && ele.prototype instanceof SvelteComponent) {
let component
return function () {
return {
c: noop,
m(target, anchor) {
component = new ele({ target, props })
mount_component(component, target, anchor, null)
},
d(detaching) {
destroy_component(component, detaching)
},
l: noop,
}
}
}
else {
return function () {
return {
c: noop,
m: function mount(target, anchor) {
insert(target, ele, anchor)
},
d: function destroy(detaching) {
if (detaching) {
detach(ele)
}
},
l: noop,
}
}
}
}
return svelteSlots
}; |
Would love to see this feature. Server side rendered Svelte does have support for Slots so I think it makes sense to have it for the client too. It's not an easy problem though An issue I'm seeing is that if you update the HTML of the slot, and you have for example an input text field with some text in it, it would reset the field to blank on update. For LiveSvelte (a Phoenix LiveView integration), here's how I did it. It's still a bit buggy as I'm doing some hacks to effectively update the slot data, and it does not solve the mentioned issue. |
Has anyone figured out yet how to do this in svelte 4?
|
They are still available, we "only" removed the type definitions to discourage its use - these internal methods will likely all change in Svelte 5 |
Is there appetite to resolve this in Svelte 5? Being able to programmatically identify elements as slots is very useful for rendering user-editable content programmatically, e.g. from a CMS, in a Svelte component. |
That is (already) possible in Svelte 5 with snippets since they can be passed as any other prop. |
I didn't appreciate the power of snippets and how much they change the game vs. slots until I looked into them to solve this problem. They make it dramatically easier and completely solve my underlying problem. Thanks @brunnerh for the tip! In particular, this is what solved the problem for me - being able to nest snippets, where I couldn't nest slots (hence trying to do something similar by setting them programmatically): {#snippet theme()}
<div
style="
position: absolute;
left: 0px;
top: 0;
width: 100%;
height: 100%;
z-index: -99;
"
>
<div style="width: 2400px; height: 500px; background: white; font-size: 240px; color: black;">
{@render layout()}
</div>
</div>
{/snippet} This could of course extend further so there's content nested inside the |
This is possible in Svelte 5 with |
In svelte 2 it was possible to pass slots option when creating new component:
In svelte 3 slots options seems doesn't work. And there is no way to set slots after component instance is created.
This is needed to properly integrate svelte components that using slots into other frameworks.
The text was updated successfully, but these errors were encountered: