问题描述
我有一个用于数字输入的文本字段组件。基本上,我只是包装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',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中的数据流。
最后,我如何才能最简单地提供一个文本框,该文本框将对要放入我的数据模型中的数字求值,而没有恼人的自动删除小数位的方法?现有功能不会自动删除结尾的字母,因此我猜想自动删除小数位是我的用户不喜欢的故意功能。
解决方法
我不确定其中的任何一个,但请考虑如何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")
会将其转换为{{1} },10.2
会将其分配给v-on:input="content = $event.target.value"
,而content
会使输入显示“ 10.2”。似乎在使输入显示“ 10.2”的最后一步失败了,因为v-bind:value="content"
的状态已正确设置为content
。如果您使用10.2
而不是<input type="text" v-model.number="content" />
,则blur一旦完成,则文本字段将成功更新为“ 10.2”。因此,看来<v-text-field v-model.number="content" />
不这样做的原因是由于Vuetify如何处理<v-text-field>
部分。 -
当您输入“ 10.2”然后输入“ B”时,开始时,
v-bind:value="content"
将通过"B10.2"
事件发出,input
将返回{{ 1}},因此parseFloat("B10.2")
修饰符将保持不变,NaN
将.number
分配给v-on:input="content = $event.target.value"
,而"B10.2"
将使输入显示“ B10.2”。我同意content
返回v-bind:value="content"
似乎不正确,而parseFloat("10.2A")
返回10.2
似乎不正确。
最后,我如何才能最简单地提供一个文本框,该文本框将对要放入我的数据模型中的数字求值,而又没有恼人的自动删除小数点的位置?
鉴于默认行为很奇怪,我认为您将不得不编写自己的自定义逻辑来转换用户的输入。例如。这样就可以将“ 10.2A”和“ B10.2”都转换为parseFloat("B10.2")
(或保持原样),以便按需要处理小数。像这样(CodePen):
"B10.2"
如果您想实际更改输入字段,似乎$forceUpdate
是必需的。但是,它似乎仅适用于10.2
,而不适用于<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>
。这与我们在第二个要点中看到的一致。您可以自定义<input>
,使其显示并像<v-text-field>
一样运作。
我将其放在<input>
内,因此用户看到“我尝试键入但被删除了”,而不是“我正在键入字符但没有出现”,因为前者的效果更好指示“您尝试输入的内容无效”的工作。
或者,您可能希望对模糊事件进行变换,而不是像键入时那样进行变换。