2

I have a problem with vue.js slots. On one hand I need to display the slot code. On the other hand I need to use it in a textarea to send it to external source.

main.vue

<template>
  <div class="main">

    <my-code>
      <template v-slot:css-code>
        #custom-css {
          width: 300px
          height: 200px;
        }
      </template>
      <template v-slot:html-code>
        <ul id="custom-css">
          <li> aaa </li>
          <li> bbb </li>
          <li> ccc </li>
        </ul>
      </template>
    </my-code>

  </div>
</template>

my-code.vue

<template>
    <div class="my-code">

        <!-- display the code -->
        <component :is="'style'" :name="codeId"><slot name="css-code"></slot></component>
        <slot name="html-code"></slot>

        <!-- send the code -->
        <form method="post" action="https://my-external-service.com/">
            <textarea name="html">{{theHTML}}</textarea>
            <textarea name="css">{{theCSS}}</textarea>
            <input type="submit">
        </form>

    </div>
</template>

<script>
export default {
    name: 'myCode',
    props: {
        codeId: String,
    },
    computed: {
        theHTML() {
            return this.$slots['html-code']; /* The problem is here, it returns vNodes. */
        },
        theCSS() {
            return this.$slots['css-code'][0].text;
        },
    }
}
</script>

The issues is that vue doesn't turn the slot content. It's an array of <VNode> elements. Is there a way to use slots inside the textarea. Or a way to retrieve slot content in the theHTML() computed property.

NOTE: I use this component in vuePress.

tony19
  • 125,647
  • 18
  • 229
  • 307
Rami
  • 73
  • 7

1 Answers1

3

You need to create a custom component or a custom function to render VNode to html directly. I think that will be the simplest solution.

vnode to html.vue

<script>
export default {
  props: ["vnode"],
  render(createElement) {
    return createElement("template", [this.vnode]);
  },
  mounted() {
    this.$emit(
      "html",
      [...this.$el.childNodes].map((n) => n.outerHTML).join("\n")
    );
  },
};
</script>

Then you can use it to your component

template>
  <div class="my-code">
    <!-- display the code -->
    <component :is="'style'" :name="codeId"
      ><slot name="css-code"></slot
    ></component>
    <slot name="html-code"></slot>

    <!-- send the code -->

    <Vnode :vnode="theHTML" @html="html = $event" />

    <form method="post" action="https://my-external-service.com/">
      <textarea name="html" v-model="html"></textarea>
      <textarea name="css" v-model="theCSS"></textarea>
      <input type="submit" />
    </form>
  </div>
</template>

<script>
import Vnode from "./vnode-to-html";
export default {
  name: "myCode",
  components: {
    Vnode,
  },
  props: {
    codeId: String,
  },
  data() {
    return {
      html: "", // add this property to get the plain HTML
    };
  },
  computed: {
    theHTML() {
      return this.$slots[
        "html-code"
      ]
    },
    theCSS() {
      return this.$slots["css-code"][0].text;
    },
  },
};
</script>

this thread might help How to pass html template as props to Vue component

kusiaga
  • 673
  • 1
  • 7
  • 18
  • You are awesome!!! Now I see the logic. Thank you for taking the time to write the answer and the long code. – Rami Nov 16 '20 at 10:07