-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
RFC: <svelte:inject> #1567
Comments
const parent = new Parent({
target: document.getElementById('target')
})
const child = parent.inject(Child, {
data: {
cool: true
}
}) and maybe support component instances as targets for sugar: const child = new Child({
target: parent,
data: {
cool: true
}
}) Sapper can maintain its own injection scheme, ASR can do its thing, and any other router should be able to work, as long as Svelte handles the "put this thing inside you. Okay, now remove it" bits. I dunno what the SSR API should be. "A component can only have one svelte:inject" sounds totally reasonable to me. With the API I imagine, requiring |
I should have fleshed this out more in the original comment, but there's a couple of reasons I didn't initially go down that road. First, you might have props and children: <svelte:inject foo={bar}>
<MrClippy message="It looks like you're trying to {goal}"/>
</svelte:inject> It feels like that stuff is easier to coordinate if it's declarative. Secondly, inside Sapper we only actually have a reference to a single component instance — the top-level I do like where your head's at though. Will have to think about whether those issues are solvable. |
Oh cool, yeah, the ability to pass things to injected components is great, I thought you were specifically avoiding that, like with slots (can we get that behavior with slots? 😃). But shouldn't that still work? Maybe I underestimate the difficulty of merging the construction options passed to I know Sapper only deals with the top-level instance now, but ASR holds on to instances at every level for a couple reasons. Routers that want to, say, change the injected component at the lowest level of the hierarchy would presumably need to hold on to every instance. // page load at /app/settings/profile
const app = new App()
const settings = app.inject(Settings)
const profile = settings.inject(Profile)
// route changes to /app/settings/emails
settings.inject(Emails) // (parent components aren't touched) |
Agreed on the desire to programmatically inject new children, it seems like that'll be way easier to integrate w/ other libraries. |
This is more like <svelte:component this={Subroute} foo={bar}>
<MrClippy message="It looks like you're trying to {goal}"/>
</svelte:component> — IOW not really a new thing from that point of view. (I remain convinced that |
The idea of resolving components from a shared global registry is a great one. What are the reasons we can't just rework |
I tried to figure out a way that it could just use There are three options:
Another nice thing about // app/manifest/client.js
const imports = [
() => import('../../routes/index.html'),
() => import('../../routes/settings/index.html'),
() => import('../../routes/settings/notifications/index.html'),
...
];
const routes = [
{
pattern: /^\/?$/,
params: () => ({}),
load: () => {
return imports[0]().then(Page => {
return { Page, children: {} };
});
}
},
{
pattern: /^\/settings\/?$/,
params: () => ({}),
load: () => {
return imports[1]().then(Page => {
return { Page, children: {} };
});
}
},
{
pattern: /^\/settings\/notifications\/?$/,
params: () => ({}),
load: () => {
return Promise.all([imports[1](), imports[2]()]).then(([Page, _1]) => {
return {
Page,
children: {
'settings/index.html': _1
}
};
});
}
},
// ...
]; I do like the idea of having an API for this stuff, as an alternative to a magic registry (though, to be clear, the registry isn't Sapper-specific — any app could do One more wrinkle. What would happen if the <!-- routes/settings/index.html -->
<div class="settings">
<SideBar/>
<Submenu/>
</div> <!-- routes/settings/_components/Submenu -->
<div class="submenu">
<svelte:inject/>
</div> That would make it a little harder to use filenames as registry keys. |
I understand where you are coming from (between and wall and a hard place 😄). I have a question though. |
I would lean towards:
|
hmm, my last post was too Sapper-centric. Svelte doesn't know about routes/directories. Put a Svelte-centric way, I would want |
Two possibilities:
|
On the naming of this special tag, I would opt for |
I'm starting to feel a little uneasy about this proposal. Apart from the magic involved (I'm not strictly opposed to magic if it significantly improves the developer experience, but I do think there should be a presumption against magic) there are some issues with it. Firstly, it would realistically only work if Secondly, I'm not sure it does what we want. Consider a very basic example — an app with three routes:
Neither So I think we might need to go back to the drawing board. My previous idea (sveltejs/sapper#262 (comment)) is starting to appeal a bit more — I just wonder if there's a smart way to retain some of the benefits we talked about (such as more granular code-splitting, non-destruction of unchanged non-leaf components) and get rid of some boilerplate. Separately, it occurs to me that having an API for this would be a bit unnecessary, since it basically already exists: <svelte:component this={SomeWellKnownProperty}/> parent.set({ SomeWellKnownProperty: Child }); |
Here's a better idea: sveltejs/sapper#262 (comment) |
The motivation behind this idea is to facilitate nested routes in Sapper, and this RFC is written with that particular use case in mind. If it seems overly Sapper-specific and needs to change, then I'm interested in thoughts as to how.
The idea is that you could have a page like this...
and another like this...
...and the second component would be injected into the first at the site of the
<svelte:inject>
, were you to visit/settings/notifications
.If there was a
routes/settings/password.html
file, it would be injected upon visiting/settings/password
and so on.In effect, it would be similar to this...
...except that
Submenu
isn't part of the the parent component's state; it would come from some global registry. (There are a number of tangible benefits to this beyond ergonomics — it enables more granular code-splitting, for example.)Implementation
If we accept two constraints...
shared: true
<svelte:inject>
...then a plausible solution would be to have the global registry be imported from inside
svelte
, and for the component's filename to be the registry key:This would cause
<svelte:inject>
to update correctly when the parent component was created, or when any state changed inside the parent component.It would not work if the value of
injector.get(file)
changed independent of a state change. For that, we would need to add some code to the component constructor:The registry would look something like this:
Inside Sapper, the components would be updated on navigation:
Does this seem workable? And is
<svelte:inject>
the right solution?(Regardless of what we decide here, it might make sense to figure out how to handle transitions between values of
xyz
in<svelte:component this={xyz}>
before going too far with the implementation, as it will no doubt be relevant.)The text was updated successfully, but these errors were encountered: