Emit truly “raw” html in Svelte

妖精的绣舞 提交于 2021-02-10 06:51:39

问题


I'm receiving raw html snippets from a headless CMS that I need to render as-is in a Sapper application.

This also includes cases where I receive an opening tag and the corresponding closing tag as two pieces of HTML between which I add a Svelte component.

<script>
    import MyComponent from './MyComponent.svelte'

    // example snippets coming from headless CMS
    const prefix = '<p class="test">' 
    const suffix = '</p>'
</script>

<!-- use snippets as-is -->
{@html prefix}
<MyComponent />
{@html suffix}

See https://svelte.dev/repl/4c1bf80ae00e476587344b6065e7346e?version=3.19.1

However, Svelte expects each item rendered with @html to be self contained element and tries to "fix" common errors. For example, it will add a closing tag to the prefix-snippet, moving the <MyComponent/>-part out of the p.

Is there a way to circumvent this behavior in general? Or more specifically - is it possible to surround rendered components with arbitrary raw HTML?

As a side node: this runs as a sapper app and the server side rendered version of the page does correctly emit the raw HTML. It's when the client side rendering kicks in that the behavior changes.


回答1:


Here's a wicked way...

DISCLAIMER Moving Svelte managed elements under its feet can't possibly be a good idea and I totally fail to appreciate what adverse side effects you might encounter next!

I especially recommend against using this in {#each} blocks, and especially keyed ones, since Svelte will want to reorder its elements and might be upset if they're not where it expects them in the DOM.

... but maybe it can get you unstuck for simple cases. You be the judge.

My idea is to render the full concatenated string (prefix + suffix) with an additional element I can grab and replace with a component already rendered independently by Svelte, outside of this fragment.

Here's the trick:

{@html prefix + '<span id="vslot"></span>' + suffix}

Here's an example component that implements the trick:

<script>
    import { afterUpdate } from 'svelte'

    export let prefix
    export let suffix

    let wrap
    let content

    afterUpdate(() => {
        const vslot = wrap.querySelector('#vslot')
        vslot.parentNode.replaceChild(content, vslot)
    })
</script>

<div bind:this={wrap}>
    {@html prefix + '<span id="vslot"></span>' + suffix}
</div>

<div bind:this={content}>
    <slot />
</div>

You would consume it like this:

<Wrapper {prefix} {suffix}>
    <MyComponent />
</Wrapper>

REPL




回答2:


You can use slots in order to achieve a similar result by wrapping component inside another component. Here is official documentation: https://svelte.dev/tutorial/slots



来源:https://stackoverflow.com/questions/60384394/emit-truly-raw-html-in-svelte

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!