使用渲染功能时 Vue 组件不显示子文本节点
Vue component not showing child text node when using render function
我想用 Vue 2 做一个可编辑的组件。它应该在任何标签中使用 contenteditable
属性,替换普通输入。我想给它一个占位符功能,以便在用户提供 none 时显示一个值,但我似乎无法让它工作。
我正在观察组件的当前值,并在没有用户内容时将 data.isEmpty
设置为 true
。该组件随后应显示占位符值,但目前它什么都不显示。
如果我 console.log
render
方法的结果,它将显示占位符子节点已正确实例化,但由于某些原因它不会显示在最终 HTML.
这是一个 JSFiddle:https://jsfiddle.net/dy27fa8t/
还有为喜欢它的人提供的嵌入式代码段:
Vue.component('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial,
isEmpty: this.initial === ''
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
input: function(event) {
self.value = event.target.innerHTML
self.$emit('edited', event.target.value)
}
}
},
this.isEmpty ? this.placeholder : this.value
)
},
watch: {
value(to, from) {
this.isEmpty = to === ''
}
}
})
new Vue({
el: '#app',
components: [
'editable-content'
]
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
如果您不将 initial
属性传递给组件,它将是未定义的。所以你应该做的检查是看是否未定义:
data() {
return {
value: this.initial,
isEmpty: typeof this.initial === 'undefined'
}
},
显然渲染 contenteditable
不符合直观的方式。相反,当内容为空时,直接使用占位符设置 innerHTML
。然后在 keydown
(输入事件之前),如果内容当前标记为空,则移除占位符。在 keyup
上(输入事件后),如果 div 仍然没有内容,再次将其标记为空(这样 shift 键不会清除占位符)。
我冒昧地使其 v-model
兼容并对占位符进行样式设置。
Vue.component('editable-content', {
props: {
value: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
isEmpty: this.value === ''
};
},
methods: {
setEmpty() {
this.$el.innerHTML = `<div contenteditable="false" class="placeholder">${this.placeholder}</div>`;
this.isEmpty = true;
},
clearEmpty() {
this.$el.innerHTML = '';
this.isEmpty = false;
}
},
mounted() {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
},
watch: {
value(newValue) {
if (newValue === '') {
this.setEmpty();
}
}
},
render: function(createElement) {
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
keydown: () => {
if (this.isEmpty) {
this.clearEmpty();
}
},
input: (event) => {
this.$emit('input', event.target.textContent);
},
keyup: () => {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
}
}
},
this.value
)
}
});
new Vue({
el: '#app',
data: {
startingBlank: '',
editedValue: 'initial value'
},
components: [
'editable-content'
]
})
.placeholder {
color: rgba(0,0,0, 0.5);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content v-model="startingBlank" placeholder="Placeholder"></editable-content>
<editable-content v-model="editedValue" placeholder="Placeholder"></editable-content>
</div>
最后我选择了混合 JS 和 CSS 解决方案,使用 :empty
伪 class。仅 Vue 的解决方法似乎太笨重,所以这感觉像是一个健康的妥协。我什至觉得没有必要再跟踪 value
。
值得注意的是,对于单文件组件,我可以使用范围 CSS,因此它更好,因为 CSS 对组件的核心功能至关重要。
Vue.component('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true,
'data-placeholder': this.placeholder
},
on: {
input: function(event) {
self.$emit('edited', event.target.value)
}
}
},
this.value
)
}
})
new Vue({
el: '#app',
components: [
'editable-content'
]
})
[data-placeholder]:empty::after {
content: attr(data-placeholder);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
我想用 Vue 2 做一个可编辑的组件。它应该在任何标签中使用 contenteditable
属性,替换普通输入。我想给它一个占位符功能,以便在用户提供 none 时显示一个值,但我似乎无法让它工作。
我正在观察组件的当前值,并在没有用户内容时将 data.isEmpty
设置为 true
。该组件随后应显示占位符值,但目前它什么都不显示。
如果我 console.log
render
方法的结果,它将显示占位符子节点已正确实例化,但由于某些原因它不会显示在最终 HTML.
这是一个 JSFiddle:https://jsfiddle.net/dy27fa8t/
还有为喜欢它的人提供的嵌入式代码段:
Vue.component('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial,
isEmpty: this.initial === ''
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
input: function(event) {
self.value = event.target.innerHTML
self.$emit('edited', event.target.value)
}
}
},
this.isEmpty ? this.placeholder : this.value
)
},
watch: {
value(to, from) {
this.isEmpty = to === ''
}
}
})
new Vue({
el: '#app',
components: [
'editable-content'
]
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>
如果您不将 initial
属性传递给组件,它将是未定义的。所以你应该做的检查是看是否未定义:
data() {
return {
value: this.initial,
isEmpty: typeof this.initial === 'undefined'
}
},
显然渲染 contenteditable
不符合直观的方式。相反,当内容为空时,直接使用占位符设置 innerHTML
。然后在 keydown
(输入事件之前),如果内容当前标记为空,则移除占位符。在 keyup
上(输入事件后),如果 div 仍然没有内容,再次将其标记为空(这样 shift 键不会清除占位符)。
我冒昧地使其 v-model
兼容并对占位符进行样式设置。
Vue.component('editable-content', {
props: {
value: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
isEmpty: this.value === ''
};
},
methods: {
setEmpty() {
this.$el.innerHTML = `<div contenteditable="false" class="placeholder">${this.placeholder}</div>`;
this.isEmpty = true;
},
clearEmpty() {
this.$el.innerHTML = '';
this.isEmpty = false;
}
},
mounted() {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
},
watch: {
value(newValue) {
if (newValue === '') {
this.setEmpty();
}
}
},
render: function(createElement) {
return createElement(
'div', {
attrs: {
contenteditable: true
},
on: {
keydown: () => {
if (this.isEmpty) {
this.clearEmpty();
}
},
input: (event) => {
this.$emit('input', event.target.textContent);
},
keyup: () => {
if (this.$el.innerHTML === '') {
this.setEmpty();
}
}
}
},
this.value
)
}
});
new Vue({
el: '#app',
data: {
startingBlank: '',
editedValue: 'initial value'
},
components: [
'editable-content'
]
})
.placeholder {
color: rgba(0,0,0, 0.5);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content v-model="startingBlank" placeholder="Placeholder"></editable-content>
<editable-content v-model="editedValue" placeholder="Placeholder"></editable-content>
</div>
最后我选择了混合 JS 和 CSS 解决方案,使用 :empty
伪 class。仅 Vue 的解决方法似乎太笨重,所以这感觉像是一个健康的妥协。我什至觉得没有必要再跟踪 value
。
值得注意的是,对于单文件组件,我可以使用范围 CSS,因此它更好,因为 CSS 对组件的核心功能至关重要。
Vue.component('editable-content', {
props: {
initial: {
type: String
},
placeholder: {
type: String,
required: false
}
},
data() {
return {
value: this.initial
}
},
render: function(createElement) {
const self = this
return createElement(
'div', {
attrs: {
contenteditable: true,
'data-placeholder': this.placeholder
},
on: {
input: function(event) {
self.$emit('edited', event.target.value)
}
}
},
this.value
)
}
})
new Vue({
el: '#app',
components: [
'editable-content'
]
})
[data-placeholder]:empty::after {
content: attr(data-placeholder);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.min.js"></script>
<div id="app">
<editable-content initial="Initial value" placeholder="Placeholder" />
</div>