【问题标题】:How can I implement v-model.number on my own in VueJS?如何在 VueJS 中自己实现 v-model.number?
【发布时间】:2021-01-06 08:15:45
【问题描述】:

我有一个用于数字输入的文本字段组件。基本上我只是包装 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 时文本正文的主体会立即反应,但当我键入“10.2A”时不会立即反应?相关地,我是如何无意中摆脱自动删除小数位的?我什至还没有到达那部分。所以我误解了Vue中的数据流。

最后,我怎样才能最简单地提供一个文本框,该文本框将评估为放入我的数据模型中的数字,但又没有烦人的小数位自动删除?现有功能不会自动删除尾随字母,所以我猜小数位的自动删除是我的用户不喜欢的故意功能。

【问题讨论】:

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


    【解决方案1】:

    我不能 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.") 会将其转换为10v-on:input="content = $event.target.value" 会将其分配给content,而v-bind:value="content" 将导致输入显示“10”。那么,这是预期的行为。

    • 当您输入“10.2”然后点击“A”时,"10.2A" 将通过input 事件发出,parseFloat("10.2A") 会将其转换为10.2v-on:input="content = $event.target.value" 会将其分配给@987654343 @ 和 v-bind:value="content" 将导致输入显示“10.2”。看起来它在导致输入显示“10.2”的最后一步失败了,因为content 的状态被正确设置为10.2。如果您使用&lt;input type="text" v-model.number="content" /&gt; 而不是&lt;v-text-field v-model.number="content" /&gt;,则一旦您使用blur,文本字段就会成功更新为“10.2”。所以看来&lt;v-text-field&gt; 没有的原因是由于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" 分配给contentv-bind:value="content" 将导致输入显示“B10.2”。我同意 parseFloat("10.2A") 返回 10.2parseFloat("B10.2") 返回 "B10.2" 似乎不正确。

    最后,我怎样才能最简单地提供一个文本框,该文本框将评估为一个数字以放入我的数据模型中,但又没有烦人的小数位自动删除?

    鉴于默认行为很奇怪,我认为您将不得不编写自己的自定义逻辑来转换用户的输入。例如。以便“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 似乎是必要的。然而,它似乎只适用于&lt;input&gt;,而不是&lt;v-text-field&gt;。这与我们在第二个要点中看到的一致。不过,您可以自定义 &lt;input&gt; 以使其显示和行为类似于 &lt;v-text-field&gt;

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

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

    【讨论】:

      猜你喜欢
      • 2020-07-27
      • 2021-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-16
      • 2015-01-11
      • 2017-09-08
      相关资源
      最近更新 更多