问题描述
我有一个包含html tag
的textarea
组件,并且我希望在此组件中以编辑模式获取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-slot
和vnode-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 intoVNode
s and makes them available in thethis.$slots.SLOTNAME
property. The default slot, naturally, goes inthis.$slots.default
. - So, in runtime, you have available to you what has been passed via
<slot>
(as VNodes inthis.$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
andvnode-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 adata
property that will be set as:value
of thetextarea
. - 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 insidetextarea-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
(orvalue
) attributes to pass the code into the components.众所周知的例子包括:
- vue-ace-editor: 此处是演示/codepen.
- Vue Prism编辑器: 此处演示 .
- vue-monaco(支持VS代码的代码编辑器): 此处演示.
- vue-codemirror : 此处演示. 这是迄今为止在github上最受欢迎的电影.
- 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组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- The
-