javascript - 使用 Vue-Treeselect 时找不到添加新项目的方法

标签 javascript vue.js autocomplete vue-treeselect

我正在尝试创建一个下拉菜单,其中包含我从后端填充的列表。这是有问题的图书馆 Vue Treeselect

一旦用户尝试输入不在内部的内容,我希望能够动态添加它,稍后在提交请求时,在后端创建该值。但是,该库似乎没有提供覆盖默认行为的方法。到目前为止,这是我尝试过的。

https://codesandbox.io/s/musing-sutherland-i5e8f?fontsize=14&hidenavigation=1&theme=dark

<template>
  <div id="app">
    <div class="container mt-4 mx-auto">
      <treeselect
        @search-change="handleSearch"
        :multiple="true"
        :options="options"
        placeholder="Select your favourite(s)..."
        no-results-text="No results found... Press enter to add"
        v-model="value"
      >
      </treeselect>

      <pre class="bg-gray-200 text-gray-600 rounded mt-4 p-4">{{
        JSON.stringify(value, null, 2)
      }}</pre>

      <h5>Search text: {{ text }}</h5>
      <button
        @click="appendNewItem"
        class="focus:outline-none text-white text-sm py-2.5 px-5 rounded-md bg-blue-500 hover:bg-blue-600 hover:shadow-lg"
      >
        Add
      </button>
    </div>
  </div>
</template>

<script>
// import the component
import Treeselect from "@riophae/vue-treeselect";
// import the styles
import "@riophae/vue-treeselect/dist/vue-treeselect.css";

export default {
  name: "App",
  components: {
    Treeselect,
  },
  data() {
    return {
      lastId: 0,
      text: "",
      value: [],
      options: [
        { id: 1, label: "Option #1" },
        { id: 2, label: "Option #2" },
      ],
    };
  },
  methods: {
    handleSearch(ev) {
      this.text = ev;
    },
    makeId() {
      return `new-item-${++this.lastId}`;
    },
    appendNewItem() {
      this.options = [...this.options, { id: this.makeId(), label: this.text }];
    },
  },
};
</script>

即使我的按钮解决方案也不起作用,因为一旦您离开树选择输入区域,文本就会重置为空字符串,因此按下按钮会添加一个空文本。

鉴于 Vue-Treeselect 的当前文档,当用户按下 enter 和treeselect 没有结果?

理想情况下,我想做这样的事情:

<treeselect (other-props)>
    <template #no-results={ node }>
       <span>No results found for {{ node.text }}</span>
       <button @click="appendNewItem">Add {{ node.text }}</button>
    </template>
</treeselect>

但是,遗憾的是,库 API 不支持此功能。它仍然没有解决“按下回车键时创建新字段”的问题,但无论如何这将是一个不错的开始。

最佳答案

不得不说,这是一个艰难的过程。

该库不提供启用您描述的功能的方法,但您可以使用一些低级 Vue API 来覆盖库方法并尝试实现您需要的效果。

本质上,我们将重写库中的 select(node) 函数,以使其适应我们的需求。

<script>
import Treeselect from "@riophae/vue-treeselect";

export default {
  extends: Treeselect,
  data() {
    return {
      overridesLastNodeId: 0,
    };
  },
  methods: {
    overridesFindValue() {
      if (this.$refs.control) {
        const childRefs = this.$refs.control.$refs;

        if (childRefs["value-container"]) {
          const valueContainer = childRefs["value-container"];

          if (valueContainer.$refs.input) {
            return valueContainer.$refs.input.value;
          }
        }
      }

      return null;
    },
    overridesCheckValueInNodes(value) {
      let childHasValue = false;

      this.traverseAllNodesDFS((node) => {
        if (node.label === value) {
          childHasValue = true;
        }
      });

      return childHasValue;
    },
    select(node) {
      /**
       * Here we override the select(node) method from
       * the library, we will inject a new node if a node
       * doesn't exist and then proxy this method to the original!
       */
      const value = this.overridesFindValue();
      if (typeof value === "string" && value.length === 0) {
        // This function gets called internally a lot, so we need
        // to make sure it's proxied when there is no value
        return Treeselect.mixins[0].methods.select.call(this, node);
      }

      if (value && value !== "") {
        if (this.overridesCheckValueInNodes(value)) {
          // If there is a value, we just fallback to the default function
          this.resetSearchQuery();
          return Treeselect.mixins[0].methods.select.call(this, node);
        }
      }

      /**
       * Finally, here's the solution to your question.
       * We can emit a new node here, call your append function
       * sending it the ID and making this work.
       */
      const id = `new-node-${++this.overridesLastNodeId}`;
      this.$emit("new-node", { value, id });

      /**
       * Additionally, to make the select select our value
       * we need to "emit" it to v-model as well
       */
      this.$emit("input", [...this.value, id]);

      /**
       * Finally, let's reset the input
       */
      this.resetSearchQuery();
    },
  },
};
</script>

然后,记得在代码中使用重写的组件:

<template>
  <div class="container mt-4 mx-auto">
    <treeselect-extended
      :multiple="true"
      :options="options"
      placeholder="Select your favourite(s)..."
      no-results-text="No results found... Press enter to add"
      v-model="value"
      @new-node="appendNewItem"
    />
    <pre class="bg-gray-200 text-gray-600 rounded mt-4 p-4">{{
      JSON.stringify(value, null, 2)
    }}</pre>
  </div>
</template>

<script>
import TreeselectExtended from "./overrides/TreeselectExtended";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";

export default {
  name: "App",
  data() {
    return {
      value: [],
      options: [
        { id: 1, label: "Option #1" },
        { id: 2, label: "Option #2" },
      ],
    };
  },
  components: {
    TreeselectExtended,
  },
  methods: {
    appendNewItem({ value, id }) {
      this.options = [...this.options, { id, label: value }];
    },
  },
};
</script>

这是一个可行的解决方案,但是,我不得不建议谨慎使用此代码,因为它会创建与库内部实现的依赖关系!这意味着,如果您从 package.json 更新库,即使在次要版本更新的情况下,您也会对您的项目进行重大更改!因为这段代码甚至依赖于库中的“私有(private)”函数,而不仅仅是面向公众的 API。您可以尝试使其面向 future ,但选择加入并使用确实满足您需求的一些不同的库可能更明智。

这是一个演示此操作的代码沙盒:Link

关于javascript - 使用 Vue-Treeselect 时找不到添加新项目的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66440337/

相关文章:

javascript - 当我将数据放在 "distribuidora"字段时,我的表没有更新

JavaScript (VueJS) 重新加载列表

vue.js - 使用一个 <transition> 包装器转换多个子元素

jquery - Vuejs 根据从下拉列表中选择的值显示其他输入字段

javascript - jquery 自动完成输入键

javascript - 当输入的数据不在数据库中时禁用按钮

c - 如何让 Komodo Edit 7 自动完成 C?

javascript - jquery - 从平面 json 创建嵌套 json

javascript - 函数调用其他函数练习我被难住了

javascript - 从给定的长日期转换为适当的日期格式