vue.js - 我如何在 VueJS 中自己实现 v-model.number?

标签 vue.js vuetify.js v-model parsefloat

我有一个用于数字输入的文本字段组件。基本上我只是包装 v-text-field 但准备自己实现它。看起来像这样。

<template>
  <v-text-field v-model.number = "content" />
</template>

<script>
export default {
    name: 'NumericTextField',
    props: [ 'value' ],
    computed: {
        content: {
            get () { return this.value },
            set (v) { this.$emit('input', f) },
        },
    }
}
</script>

这引起了用户反馈,当文本字段中包含字符串“10.2”然后在“2”上退格,然后小数位被自动删除时,这很烦人。我想改变这个行为,让“10”。保留在文本字段中。我还想从第一原则来理解这一点,因为我对 Vue 还比较陌生。

所以我把它作为第一次尝试,这是我尝试过的最有启发性的事情。

<template>
  <v-text-field v-model="content" />
</template>

<script>
export default {
    name: 'NumericTextField',
    props: [ 'value' ],
    computed: {
        content: {
            get () { return this.value },
            set (v) {
                console.log(v)
                try {
                    const f = parseFloat(v)
                    console.log(f)
                    this.$emit('input', f)
                } catch (err) {
                    console.log(err)
                }
            },
        },
    }
}
</script>

我读到 v-model.number 是基于 parseFloat 的,所以我想一定会发生这样的事情。所以它确实解决了小数位被自动删除的问题。但是......它甚至不会自动删除多余的字母。因此,如果我输入“10.2A”,即使我看到打印出“10.2”的控制台日志,“A”也会保留。此外,还有一个更严重的错误特征。当我移动到字符串的开头并将其更改为“B10.2”时,它立即被替换为“NaN”。

所以我很想知道很多事情。为什么文本正文在我更改为 NaN 时会立即 react ,但在我键入“10.2A”时不会立即 react ?相关地,我是如何无意中摆脱自动删除小数位的?我什至还没有到那个部分呢。所以我误解了 Vue 中的数据流。

最后,我怎样才能最简单地提供一个文本框来计算一个数字以放入我的数据模型,而不是让烦人的小数位自动删除?现有功能不会自动删除尾随字母,所以我猜测自动删除小数位是我的用户不喜欢的故意功能。

最佳答案

我不是 100% 确定这些,但请考虑如何 v-model works on components .它基本上是这样做的:

<v-text-field
  v-bind:value="content"
  v-on:input="content = $event.target.value" 
/>

并考虑如何 .number modifier works .它通过 parseFloat 运行输入, 但如果 parseFloat不起作用,它保持原样。

因此,有了这样的理解,我会期待以下内容:

  • 当您输入“10.2”然后按退格键时,"10."将通过 input 发出事件,parseFloat("10.")会将其转换为 10 , v-on:input="content = $event.target.value"会将其分配给 content , 和 v-bind:value="content"会导致输入显示“10”。那么,这是预期的行为。

  • 当您输入“10.2”然后点击“A”时,"10.2A"将通过 input 发出事件,parseFloat("10.2A")会将其转换为 10.2 , v-on:input="content = $event.target.value"会将其分配给 content , 和 v-bind:value="content"会导致输入显示“10.2”。看起来它在导致输入显示“10.2”的最后一步失败了,因为 content 的状态正确设置为 10.2 .如果你使用 <input type="text" v-model.number="content" />而不是 <v-text-field v-model.number="content" /> ,一旦你blur ,文本字段成功更新为“10.2”。所以看来 <v-text-field> 的原因不是因为 Vuetify 如何处理 v-bind:value="content"部分。

  • 当您输入“10.2”然后输入“B”时,在开头,"B10.2"将通过 input 发出事件,parseFloat("B10.2")会返回 NaN ,因此 .number修饰符会保持原样,v-on:input="content = $event.target.value"将分配 "B10.2"content , 和 v-bind:value="content"会导致输入显示“B10.2”。我同意这似乎不适合 parseFloat("10.2A")返回 10.2但是parseFloat("B10.2")返回 "B10.2" .

Lastly, how can I most simply provide a text box that's going to evaluate to a number for putting into my data model but not have the annoying auto delete of decimal places?

鉴于默认行为很奇怪,我认为您将不得不编写自己的自定义逻辑来转换用户的输入。例如。这样“10.2A”和“B10.2”都转换为10.2 (或保持原样),以便按照您的意愿处理小数。像这样 ( CodePen ):

<template>
  <div id="app">
     <input 
       v-bind:value="content"
       v-on:input="handleInputEvent($event)"
     />
    <p>{{ content }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      content: 0,
    };
  },
  methods: {
    handleInputEvent(e) {
      this.content = this.transform(e.target.value);
      setTimeout(() => this.$forceUpdate(), 500);
    },
    transform(val) {
      val = this.trimLeadingChars(val);
      val = this.trimTrailingChars(val);
      // continue your custom logic here
      
      return val;
    },
    trimLeadingChars(val) {
      if (!val) {
        return "";
      }
      
      for (let i = 0; i < val.length; i++) {
        if (!isNaN(val[i])) {
          return val.slice(i);
        }
      }
      
      return val;
    },
    trimTrailingChars(val) {
      if (!val) {
        return "";
      }
      
      for (let i = val.length - 1; i >= 0; i--) {
        if (!isNaN(Number(val[i]))) {
          return val.slice(0,i+1);
        }
      }
      
      return val;
    },
  },
};
</script>

$forceUpdate 如果您希望输入字段实际发生变化,这似乎是必要的。但是,它似乎只适用于 <input> , 不是 <v-text-field> .这与我们在第二个要点中看到的一致。您可以自定义 <input>让它看起来和表现得像<v-text-field>虽然。

我把它放在 setTimeout 里面所以用户看到“我试图输入这个但它被删除了”而不是“我正在输入字符但它们没有出现”因为前者在指示“你试图输入的内容无效”方面做得更好。

或者,您可能希望对模糊事件进行转换,而不是在输入时进行转换。

关于vue.js - 我如何在 VueJS 中自己实现 v-model.number?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63971870/

相关文章:

vue.js - 用 cypress 测试 vuex

vue.js - 如何从 Vue CLI 构建中排除静态文件?

javascript - vuetify v-select 具有多个选项 - 获取选择/取消选择选项

vue.js - 来自 Vue.js 的 ESLint 插件的消息中的 'LHS' 是什么意思?

javascript - 如果在同一 route ,VueJS 会重新加载

vue.js - vue-typeahead 说你需要提供一个 HTTP 客户端

vue.js - 在 vuetify 2.x 中的 v-autocomplete 下拉项中显示工具提示

vue.js - 排序 v-data-table 检查复选框

javascript - 为什么 @change 或 @input 可以与自定义 v-model 组件一起使用?

vue.js - 如何在 vue 3 的插槽上使用 v-model?