Skip to content
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

Support for "High Order Component" / Wrapper / Forwarding #5106

Open
j3rem1e opened this issue Jul 6, 2020 · 7 comments
Open

Support for "High Order Component" / Wrapper / Forwarding #5106

j3rem1e opened this issue Jul 6, 2020 · 7 comments

Comments

@j3rem1e
Copy link

j3rem1e commented Jul 6, 2020

Is your feature request related to a problem? Please describe.

I want to build a "transparent" lazy-loading component. This component should load a target component asynchronously, and when ready, delegate everything to this target.

This lazy-component should be generic: it should work for every target component. Moreover, I don't want the consumer of the component to know there is this wrapper, it should be "transparent" for it.

Today, it doesn't seem to be possible to build such "wrapper" because :

  • There is no API to forwards slots to the delegate ;
  • There is no API to forwards events to the delegate ;
  • There is no API to forwards "bind" from the parent to the delegate ;

There is various issues about this :
#2837 about forwardings events ;
#1824 and #4295 about forwardings slots ;
I found nothing about "binding" forwardings.
#4647 is about a loadable component, closed because "svelte-loadable" can implements this issue. however, it's false because it doesn't supports slots or events.

Describe the solution you'd like

An official way/api to "wrapper" transparently another component.

Describe alternatives you've considered

I am able to prototype such wrapper with internals API, however I am not able to forwards binding from parent to child :

<svelte:component this={cpn} {...slotsProps} {...$$restProps} bind:this={instance}/>

<script>
    import { get_current_component } from 'svelte/internal';
    
    export let provider;
    
    const slotsProps = {"$$slots":$$props.$$slots, "$$scope":$$props.$$scope};
    const self = get_current_component();
    
    let cpn;
    let instance;
        
    provider().then(result => cpn = result);
    
    $: if (instance) {
        for (let [type, listeners] of Object.entries(self.$$.callbacks)) {
            instance.$on(type, (e) => {
                listeners.forEach(l => l(e));
            });
        }
    }
</script>

How important is this feature to you?

I can't today implements this kind of component. I'd like to "wrap" heavyweight components without updating or adding loading logic to every consumer.

@pospi
Copy link

pospi commented Jul 31, 2020

I just ran up against this trying to implement React-like patterns in Svelte. I'm curious what the appropriate idiom to follow here is.

In React apps, it was quite common for me to implement "controller" components which have dynamic children. Such controllers were usually for fetching external data (example: the active user ID) and passing it down to a dynamic child component. So basically, any pure view component which knows how to render something about the active user could be wrapped in one of these "controllers" to have the user ID dynamically assigned.

The naive approach I tried looks like this:

<script>
  import { getClient, query } from 'svelte-apollo'

  import { queryMyAgent } from './queries.ts'

  const client = getClient()
  const agent = query(client, { query: queryMyAgent })
</script>

{#await $agent}
  Loading...
{:then result}
  <slot contextAgent={result.data.myAgent.id}></slot>
{:catch error}
  Agent loading failed: {error}
{/await}

But the nested component does not seem to get its own contextAgent prop assigned via the slot. Which means that this boilerplate would have to be included directly in every component that wishes to inject a context agent, correct? Not ideal.

@stephane-vanraes
Copy link
Contributor

@pospi that sounds more like a support question than an issue, which you should try using the discord chat for. But you can achieve this by using the slot let: bindings: https://svelte.dev/docs#slot_let

@pospi
Copy link

pospi commented Jul 31, 2020

Thanks for that @stephane-vanraes. It looks like you need to unpack the promise to get at the real value in order for slot let: to expose the binding. But it works, and the boilerplate is only in one component now which is nice:

<script>
  import { getClient, query } from 'svelte-apollo'

  import { queryMyAgent } from './queries.ts'

  const client = getClient()
  const agent = query(client, { query: queryMyAgent })

  let loading = true
  let contextAgent
  let error

  agent.subscribe(promise => {
    promise
      /* eslint no-return-assign: 0 */
      .then(val => contextAgent = val)
      .catch(e => error = e)
      .finally(() => loading = false)
  })
</script>

{#if loading}
  Loading...
{:else if error}
  Agent loading failed: {error}
{:else}
  <slot {contextAgent}></slot>
{/if}

Should I be able to bind to the reactive props directly?

@janosh
Copy link
Contributor

janosh commented Dec 16, 2020

I've only started using Svelte recently but I've already run into the need to forward event bindings a few times. See e.g. codefeathers/rollup-plugin-svelte-svg#11. Would be great to know if this is on the roadmap or what's preventing it.

@stale
Copy link

stale bot commented Jun 26, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@sxxov
Copy link

sxxov commented Aug 1, 2021

I've written an RFC at sveltejs/rfcs#57 in an attempt to propose a solution for this. Anyone one looking at this problem should feel free to chime in.

@j3rem1e
Copy link
Author

j3rem1e commented Dec 5, 2023

Svelte 5 solves the slot/events forwarding. But AFAIK it's not possible to implement the "bind forwarding".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants