使用渲染函数时,Vue组件未显示子文本节点

时间:2017-05-01 14:43:56

标签: javascript vue.js vuejs2 vue-component

我尝试使用Vue 2进行可编辑的组件。它应该在任何标记中使用contenteditable属性,替换正常输入。我想给它一个占位符功能,以便在用户没有提供时显示值,但我似乎无法使其正常工作。

我正在观看组件的当前值,并在没有用户内容时将data.isEmpty设置为true。然后该组件应显示占位符值,但目前它没有显示任何内容。

如果我console.log render方法的结果,它将显示占位符子节点已正确实例化,但由于某种原因它只是在最终HTML上显示。< / p>

这是一个JSFiddle:https://jsfiddle.net/dy27fa8t/

为喜欢它的用户提供了一个嵌入代码段:

&#13;
&#13;
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'
  ]
})
&#13;
<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>
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:2)

显然渲染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>

答案 1 :(得分:2)

最后,我使用:empty伪类来解决混合JS和CSS解决方案。仅仅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>

答案 2 :(得分:1)

如果你没有将initial道具传递给组件,它将是未定义的。因此,您应该检查是否未定义:

data() {
    return {
      value: this.initial,
      isEmpty: typeof this.initial === 'undefined'
    }
  },