首页 > 解决方案 > 如何有条件地在作用域 Vue 组件中附加元素?

问题描述

我正在尝试为双击时可以编辑的标题创建一个组件。组件将应使用的 h-tag 和 title 作为 props,并应生成一个正常的 h-tag,双击后将变为输入字段。如果页面上只有一个标题,这已经有效,但是一旦在一页上使用了多个组件,它就会中断,因为组件的范围不正确。但我不知道怎么做。这是代码:

<template>
  <div class="edit-on-click">
    <input
      :class="sizeClass"
      type="text"
      v-if="edit"
      v-model="editedTitle"
      @blur="finishEdit"
      @keyup.enter="finishEdit"
      v-focus="true"
    />
    <span v-show="!edit" @dblclick.prevent="edit = true"></span>
  </div>
</template>

安装的钩子我不知道如何确定范围:

  mounted() {
    let node = document.createElement(this.size); // Takes h-tag (h1, h2 etc.)
    let titleText = document.createTextNode(this.finalTitle); // Takes title

    node.appendChild(titleText);
    node.classList.add("editable-title");

    // This breaks the code once there are multiple components in the document
    document.getElementsByTagName("span")[0].appendChild(node);
  },

如何以有效的方式确定范围?非常感谢您!

标签: javascriptvue.jsdom

解决方案


好吧,使用 Vue,您可能希望尽可能避免以“本机”方式创建 DOM 元素,因为您可能会遇到竞争条件,其中 Vue 不知道这些元素的存在,您可能希望在某些时候做出反应时间(在你的情况下,<span>双击)。

相反,您可以做的可能是使用 this<component>v-bind:isprop 动态地“在”这些不同的标题之间“切换”。考虑以下示例:

Vue.component('EditableHeading', {
  template: '#editable-heading',

  props: {
    size: {
      type: String,
      default: 'h1'
    },
    value: {
      type: String,
      required: true
    }
  },

  data() {
    return {
      editing: false
    }
  },

  methods: {
    confirm(e) {
      this.$emit('input', e.target.value);
      this.close();
    },
    start() {
      this.editing = true;
      
      this.$nextTick(() => {
        this.$el.querySelector('input[type="text"]').select();
      });
    },
    close() {
      this.editing = false;
    }
  }
})

new Vue({
  el: '#app',

  data: () => ({
    titleList: [],
    text: 'New Title',
    size: 'h3'
  }),

  methods: {
    addNewTitle() {
      this.titleList.push({
        text: this.text,
        size: this.size
      });
    }
  }
})
.edit-on-click {
  user-select: none;
}

.heading-size {
  margin-top: 1rem;
  width: 24px;
}

p.info {
  background-color: beige;
  border: 1px solid orange;
  color: brown;
  padding: 4px 5px;
  margin-top: 2rem;
}
<script src="https://vuejs.org/js/vue.min.js"></script>

<div id="app">
  <editable-heading 
    v-for="(title, index) of titleList" :key="index" 
    v-model="title.text" 
    :size="title.size">
  </editable-heading>

  <div>
    <label>
      Heading size: 
      <input v-model="size" class="heading-size" />
    </label>
  </div>
  <div>
    <label>
      Title: 
      <input v-model="text" />
    </label>
  </div>
  <div>
    <button @click="addNewTitle()">Add new title</button>
  </div>

  <p class="info">
    [double-click]: Edit <br />
    [enter]: Confirm <br />
    [esc/mouseleave]: Cancel
  </p>
</div>

<script id="editable-heading" type="text/x-template">
  <div class="edit-on-click">
    <input 
      type="text" 
      v-if="editing" 
      :value="value" 
      @blur="close" 
      @keydown.enter="confirm" 
      @keydown.esc="close" />

    <component :is="size" v-else @dblclick="start">{{value}}</component>
  </div>
</script>


推荐阅读