sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.57k stars 4.12k forks source link

Render functions #4971

Closed dasDaniel closed 4 years ago

dasDaniel commented 4 years ago

Is your feature request related to a problem? Please describe. I would like to use functions to define content to render. This would allow me to have DOM content configurable within an array.

Describe the solution you'd like Vue has the h function that can be used to render the this content, something similar would be great, but I'm not sure that it is possible given that svelte does this compile time.

Describe alternatives you've considered This is the current solution I'm using @html with template literals

        <tr>
          {#each columns as col}
            <td>{@html col.renderValue }</td>
          {/each}
        </tr>

where render value is


      renderValue: v => {
        const className = v.red ? 'red' : 'not-red';
        return `<span class="${className}">${v.icon}</span>`;
      }

How important is this feature to you? I can do without it

Additional context repo/code

Conduitry commented 4 years ago

This basically sounds like it's either asking for JSX, or it's asking for a built-in helper function that calls .join('') on an array. If one of these is indeed what this is asking for, that isn't going to happen. Because it operates at compile time, Svelte operates under different restrictions.

dasDaniel commented 4 years ago

I thought so, but I'm wondering if there is a way to support it compile time?

Assuming I've got a bunch of these definitions and they can get a lot more complex, is there a way that a svelte template can be used but generated on compile-time (like JSX)?

Looks like MDsveX is doing something like that. (though I could be wrong)

antony commented 4 years ago

It's worth noting that the proposed render function would require Svelte to render a string of html (which is not how Svelte works), parse it, then validate it against Svelte's own element structure, to validate attributes and such.

This approach to building elements is basically the opposite to how Svelte works, so if this was a desired behaviour, it would probably be a good indicator that Svelte wasn't a good fit for that person's use-case.

Perhaps @pngwn can clarify what MDsveX does, but I feel that this issue can be closed, since as Conduitry says, it's not something we'd look to support.

Conduitry commented 4 years ago

Yeah, closing. The concept of render functions is anti-Svelte.

pngwn commented 4 years ago

Mdsvex isn’t doing anything like this at all. Mdsvex is just converting markdown syntax to HTML and returning a valid svelte component.

A render function makes no sense because it dynamically builds the DOM at runtime which is counter to Svelte’s goals.

Dynamic elements would provide some of this behaviour when implemented but will naturally come with some optimisation losses. The change in syntax itself is probably never going to happen.

SystemParadox commented 4 years ago

Rather than just closing this as "anti-Svelte", can we have more of discussion about how we should go about achieving this in a "Svelte way"? Perhaps leading to a blog post or a tutorial entry to point people to in the future? This seems like it could be quite a common question, particularly for users coming from React or Vue.

I'm a long time React user who's been excited about Svelte for a while. I went through the complete tutorial and this was literally the only sticking point. It's really not clear from the tutorial or any other information I could find how you would go about creating complex reusable components like tables.

As a first-effort towards a potential solution, this is what I have managed to come up with:

// Table.svelte
<script>
  export let columns, data;
</script>

<table>
  <tr>
    {#each columns as col}
      <th>{ col.label }</th>
    {/each}
  </tr>
  {#each data as row}
    <tr>
      {#each columns as col}
        <slot name='cell' col={ col } row={ row }>
          <td>{ row[col.key] }</td>
        </slot>
      {/each}
    </tr>
  {/each}
</table>
// App.svelte
<script>
  import Table from './Table.svelte';
  let columns = [
    { key: 'id', label: 'ID' },
    { key: 'name', label: 'Name' },
  ];
  let data = [
    { id: 1, name: 'A' },
    { id: 2, name: 'B' },
  ];
</script>

<Table {columns} {data}>
  <td slot='cell' let:row={ row } let:col={ col }>
    {#if col.key == 'id'}
      <td>ID: { row.id }</td>
    {:else if col.key == 'name'}
      <td>Name: { row.name }</td>
    {/if}
  </td>
</Table>

Is this the best way to achieve this? Is there any way to do it without the if-else tree?

If we had dynamic slot names the table could do something like <slot name={ col.key }> and then we could avoid the if-else tree by using different slots like this:

<Table {columns} {data}>
  <td slot='id' let:row={ row }>
    ID: { row.id }
  </td>
  <td slot='name' let:row={ row }>
    Name: { row.name }
  </td>
</Table>

Any thoughts?

antony commented 4 years ago

Hi @SystemParadox - we close issues as not Svelte when they are feature requests / suggestions that are not suitable or desirable for Svelte.

We don't turn the issue into a discussion or a tutorial because github issues is a place we like to reserve for bug reports, enhancements, or feature requests for Svelte, and discussions and support issues are better held on discord, where you can get more "realtime" support and discussion.

We also have a Svelte REPL where concepts such as the one you have just shown above can be coded interactively, as a much better demonstration of a concept than un-highlighted code on github allows. This allows people to better understand your question and help you.

If you want to come and chat to us, and the wider community, we'll be more than happy to help you, and perhaps if your feature doesn't exist, could be turned into an RFC for discussion and possible addition to the Svelte API.

andykras commented 3 years ago

What if I want to migrate to Svelte, and I have a several Vue components for doing layout on a page using render function. Do I need to rewrite it from scratch?

antony commented 3 years ago

To use Vue in a Svelte app, use Vue as you normally would.

To use Svelte in a Vue App, compile your component as normal, and call it as a constructor:

new MyComponent({ target: element })

Svelte does not need and will not get a render method, as it is not necessary.

andykras commented 3 years ago

To use Vue in a Svelte app, use Vue as you normally would.

To use Svelte in a Vue App, compile your component as normal, and call it as a constructor:

new MyComponent({ target: element })

Svelte does not need and will not get a render method, as it is not necessary.

Thanks!

sirish-chen commented 3 years ago

To use Vue in a Svelte app, use Vue as you normally would.

To use Svelte in a Vue App, compile your component as normal, and call it as a constructor:

new MyComponent({ target: element })

Svelte does not need and will not get a render method, as it is not necessary.

Is it possible to pass vue-component to svelte-component in a Vue App ?

binajmen commented 2 years ago

This seems like it could be quite a common question, particularly for users coming from React or Vue.

Two years later, here I am 😄 Coming from React, I understand this is not Svelte compliant, but having an explanation how to achieve the same result (or why I can't achieve that) would be nice!

@SystemParadox did you improve your suggestion since?

atsepkov commented 1 year ago

You could technically achieve the same result with vanillajs in a svelte file that builds the element dynamically via createElement (https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement), but as others have already said, you get none of the benefits of svelte when manipulating this element.