本文介绍了如何将HTML模板作为道具传递给Vue组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含html tagtextarea组件,并且我希望在此组件中以编辑模式获取html.我使用Laravel生成html.

I have a textarea component that include html tag and I want to get html in edit mode in this component. I use Laravel to generate html.

<template>
    <div>
        <textarea
                  :value="content"
                  :name="name"
                  :id="id">
              <slot></slot>
        </textarea>
    </div>
</template>

在刀片页面中,我曾经使用过此组件:

In blade page I used to this component:

<my-component>
   <p class="textbox">hello world</p>
</my-component>

当我将此组件放在页面中时,请在文本区域中显示标签<slot></slot>.我该怎么办?您有我需要的解决方案吗?

when I put this component in page show me tag <slot></slot> in textarea. What should I do? Do you have any solution for my need?

谢谢

推荐答案

<textarea>组件被Vue渲染器视为静态组件,因此将它们放入DOM后,它们根本不会发生变化(因此,这就是为什么如果您检查DOM,则会在<textarea>的内部看到<slot></slot>.

<textarea> components are treated as static by the Vue renderer, thus after they are put into the DOM, they don't change at all (so that's why if you inspect the DOM you'll see <slot></slot> inside your <textarea>).

但是即使改变了,也无济于事.只是因为<textarea>中的HTML元素没有成为它们的值.您必须设置 TextArea元素的value属性使它正常工作.

But even it if they did change, that wouldn't help much. Just because HTML elements inside <textarea>s don't become their value. You have to set the value property of the TextArea element to make it work.

无论如何,请不要绝望.这是可行的,您需要克服的上述问题就是发挥一个小的辅助组件的作用.

Anyway, don't despair. It is doable, all you need to overcome the issues above is to bring a small helper component into play.

有很多方法可以实现这一目标,如下所示.它们在本质上有不同,您希望原始组件的模板是这样.

There are many possible ways to achieve this, two shown below. They differ basically in how you would want your original component's template to be.

您组件的模板现在将变为:

Your component's template would now become:

<template>
    <div>
      <textarea-slot
                v-model="myContent"
                :name="name"
                :id="id">
            <slot></slot>
      </textarea-slot>
    </div>
</template>

如您所见,除了用<textarea-slot>替换<textarea>外,什么都没有改变.这足以克服Vue对<textarea>进行的静态处理. <textarea-slot>的完整实现在下面的演示中.

As you can see, nothing but replacing <textarea> with <textarea-slot> changed. This is enough to overcome the static treatment Vue gives to <textarea>. The full implementation of <textarea-slot> is in the demo below.

解决方案是创建一个助手组件(在下面命名为vnode-to-html),该组件将转换插槽的 VNodes 转换为HTML字符串.然后,您可以将此类HTML字符串设置为<textarea>value.您组件的模板现在将变为:

The solution is to create a helper component (named vnode-to-html below) that would convert your slot's VNodes into HTML strings. You could then set such HTML strings as the value of your <textarea>. Your component's template would now become:

<template>
    <div>
        <vnode-to-html :vnode="$slots.default" @html="valForMyTextArea = $event" />
        <textarea
                  :value="valForMyTextArea"
                  :name="name"
                  :id="id">
        </textarea>
    </div>
</template>

在这两种选择中...

my-component的用法保持不变:

In both alternatives...

The usage of the my-component stays the same:

<my-component>
   <p class="textbox">hello world</p>
</my-component>


Vue.component('my-component', {
  props: ["content", "name", "id"],
  template: `
      <div>
          <textarea-slot
                    v-model="myContent"
                    :name="name"
                    :id="id">
                <slot></slot>
          </textarea-slot>


          <vnode-to-html :vnode="$slots.default" @html="valueForMyTextArea = $event" />
          <textarea
                    :value="valueForMyTextArea"
                    :name="name"
                    :id="id">
          </textarea>
      </div>
  `,
  data() { return {valueForMyTextArea: '', myContent: null} }
});

Vue.component('textarea-slot', {
  props: ["value", "name", "id"],
  render: function(createElement) {
    return createElement("textarea",
      {attrs: {id: this.$props.id, name: this.$props.name}, on: {...this.$listeners, input: (e) => this.$emit('input', e.target.value)}, domProps: {"value": this.$props.value}},
      [createElement("template", {ref: "slotHtmlRef"}, this.$slots.default)]
    );
  },
  data() { return {defaultSlotHtml: null} },
  mounted() {
    this.$emit('input', [...this.$refs.slotHtmlRef.childNodes].map(n => n.outerHTML).join('\n'))
  }
});

Vue.component('vnode-to-html', {
  props: ['vnode'],
  render(createElement) {
    return createElement("template", [this.vnode]);
  },
  mounted() {
    this.$emit('html', [...this.$el.childNodes].map(n => n.outerHTML).join('\n'));
  }
});

new Vue({
  el: '#app'
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <my-component>
    <p class="textbox">hell
      o world1</p>

    <p class="textbox">hello world2</p>
  </my-component>
</div>

故障:

  • Vue将<slot>解析为 ,并使其在this.$slots.SLOTNAME属性中可用.自然,默认插槽位于this.$slots.default.
  • 因此,在运行时中,您可以使用通过<slot>传递的内容(作为this.$slots.default中的VNode).现在的挑战变成了如何将这些VNode转换为HTML字符串?.这是一个复杂的仍未解决,问题,将来可能会获得不同的解决方案,但是即使有,这很可能需要一段时间..
  • 以上两个解决方案(template-slotvnode-to-html)都使用Vue的render函数将VNode渲染到DOM,然后拾取渲染的HTML.
  • 由于提供的插槽可能具有任意HTML,因此我们将VNode渲染到 HTML模板元素,该元素不会执行任何<script>标记.
  • 两种解决方案之间的区别在于它们如何处理"从渲染函数生成的HTML.
    • vnode-to-html作为事件返回,应由父级(my-component)拾取,该父级使用传递的值来设置data属性,该属性将被设置为textarea:value.
    • textarea-slot向父级声明自己为<textarea>.这是一种更清洁的解决方案,但需要更多注意,因为您必须指定要传递给在textarea-slot内部创建的<textarea>的属性.
    • Vue parses the <slot>s into VNodes and makes them available in the this.$slots.SLOTNAME property. The default slot, naturally, goes in this.$slots.default.
    • So, in runtime, you have available to you what has been passed via <slot> (as VNodes in this.$slots.default). The challenge now becomes how to convert those VNodes to HTML String? This is a complicated, still open, issue, which may get a different solution in the future, but, even if it ever does, it will most likely take a while.
    • Both solutions above (template-slot and vnode-to-html) use Vue's render function to render the VNodes to the DOM, then picks up the rendered HTML.
    • Since the supplied slots may have arbitrary HTML, we render the VNodes into an HTML Template Element, which doesn't execute any <script> tags.
    • The difference between the two solutions is just how they "handle back" the HTML generated from the render function.
      • The vnode-to-html returns as an event that should be picked up by the parent (my-component) which uses the passed value to set a data property that will be set as :value of the textarea.
      • The textarea-slot declares itself a <textarea>, to the parent doesn't have to. It is a cleaner solution, but requires more care because you have to specify which properties you want to pass down to the <textarea> created inside textarea-slot.


      但是,重要的是要知道Vue在将声明的<template>解析为<slot>时,会剥离一些格式信息,例如顶级组件之间的空格.同样,它会剥离<script>标记(因为它们不安全).这些是使用<slot>的任何解决方案所固有的警告(此处未显示).所以要注意.

      However possible, it is important to know that Vue, when parsing the declared <template> into <slot>s, will strip some formatting information, like whitespaces between top-level components. Similarly, it strips <script> tags (because they are unsafe). These are caveats inherent to any solutions using <slot>s (presented here or not). So be aware.

      Vue的典型富文本编辑器通过使用v-model(或value)属性将代码传递到组件中,从而完全解决了此问题.

      Typical rich text editors for Vue, work around this problem altogether by using v-model (or value) attributes to pass the code into the components.

      众所周知的例子包括:

      • vue-ace-editor: Demo/codepen here.
      • Vue Prism Editor: Demo here.
      • vue-monaco (the code editor that powers VS Code): demo here.
      • vue-codemirror: Demo here. This is by far the most starred on github.

      他们在他们的网站上都有非常好的文档(如上链接),所以在这里重复它们对我没有多大用处,但仅作为示例,请参见codemirror如何使用value道具来传递代码:

      They all have very good documentation in their websites (linked above), so it would be of little use for me to repeat them here, but just as an example, see how codemirror uses the value prop to pass the code:

      <codemirror ref="myCm"
                  :value="code"
                  :options="cmOptions"
                  @ready="onCmReady"
                  @focus="onCmFocus"
                  @input="onCmCodeChange">
      </codemirror>
      

      所以他们就是这样做的.当然,如果<slot> s(带有警告)适合您的用例,则也可以使用它们.

      So that's how they do it. Of course, if <slot>s - with its caveats - fit your use case, they can be used as well.

      这篇关于如何将HTML模板作为道具传递给Vue组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-13 04:40