Vue + Vuex:如果商店没有更改,商店不会设置输入值
Vue + Vuex: input value not set by store if no store change
<template>
<input
@input="formatValue"
type="text"
:value="formattedValue"
/>
</template>
<script type="text/javascript">
import {formatPhoneNumber} from '~/utils/string';
export default {
computed: {
formattedValue: function(){
return formatPhoneNumber(this.value)
},
},
methods: {
formatValue(e) {
this.$emit('input', formatPhoneNumber(e.target.value))
}
},
props: ['value']
}
</script>
只要 formatPhoneNumber(value)
产生不同的值,一切正常,但一旦达到最大长度(自 formatPhoneNumber('xx xx xx xx xx whatever') == 'xx xx xx xx xx'
起),发出的值与当前存储相同一.
完全没问题,只是结果状态没有发生变化,组件也没有重新渲染,因此 formattedValue()
没有被调用。
所以我最终在商店中得到 xx xx xx xx xx
,但输入显示 xx xx xx xx xx whatever
,因为本地输入值与商店不同。
如何避免这种意外行为?将 formatPhoneNumber()
移动到商店并不能解决我的问题,因为它仍然可以防止突变,并且仅在 formattedValue()
中使用 formatPhoneNumber()
会使我在商店中得到一个未格式化的值也不是我想要的。
为什么带有动态 value
集的 Vue input
仍然管理本地状态?
为了实现你想要的(我认为),你可以将 formatValue 方法更改为
formatValue(e) {
this.$emit('input', e.target.value = formatPhoneNumber(e.target.value));
}
以便它将输入设置为格式化的 phone 数值。您将以一种或另一种方式覆盖输入产生的内容,因此您最好在输入事件上执行此操作。
我会使用 v-model 而不是 v 值,因为这样可以让我完全控制要在输入字段中显示的内容。
通过这种方式,您可以格式化输入值,然后将其设置回模型中。它看起来像这样:
<template>
<input @input="formatValue" type="text" v-model="inputModel">
</template>
<script type="text/javascript">
export default {
data() {
return {
inputModel: this.value
};
},
methods: {
formatValue() {
this.inputModel = formatPhoneNumber(this.inputModel);
this.$emit("input", this.inputModel);
}
},
props: ["value"]
};
</script>
这是我创建的 working example 来测试它。
我认为最简单的方法是对父级的@input 事件进行简单的一行修改,在更新它之前清除 prop 值。
你仍然只需要发出一个值,但在使用发出的值之前,清除 prop。
我在下面提供了一个片段(但请注意片段中的其他差异):
我没有指定输入字段值,而是选择使用 v-model 将其绑定到具有 get 和 set 方法的计算 属性。这允许我在访问和修改数据时使用不同的逻辑(在许多情况下非常方便)。
通过分离此逻辑,我能够将功能从输入事件内部移至 set 方法,并完全消除输入事件。
new Vue({
el: "#app",
// props: ['valueProp'],
data: {
valueProp: "" //simulate prop data
},
computed: {
// --Value input element is binded to--
inputValue:{
get(){ //when getting the value, return the prop
return this.valueProp;
},
set(val){ //when the value is set, emit value
this.formatValue(val);
}
}
},
methods: {
// --Emit the value to the parent--
formatValue(val) {
this.parentFunction(this.formatPhoneNumber(val)); //simulate emitting the value
// this.$emit('input', formatPhoneNumber(val));
},
// --Simulate parent receiving emit event--
parentFunction(emittedValue){
console.log("emitted:" + emittedValue);
this.valueProp = null; //first clear it (updates the input field)
this.valueProp = emittedValue; //then assign it the emitted value
},
// --Simulate your format method--
// THIS LOGIC CAN BE IGNORED. It is just a quick implementation of a naive formatter.
// The "important" thing is it limits the length, to demonstrate exceeding the limit doesn't get reflected in the input field
formatPhoneNumber(val){
var phoneSpaces = [2,4,6,8]; //specify space formatting (space locations)
var maxLength = 10; //specify the max length
val = val.replace(/ /g,''); //remove existing formatting
if(val.length > maxLength) //limits the length to the max length
val = val.substring(0, maxLength);
// for the number of desired spaces, check each space location (working backwards) ... if value is longer than space location and space location is not a space ... add a space at the location.
for(var i = phoneSpaces.length-1; i >= 0; i--){
if(val.length > phoneSpaces[i] && val[phoneSpaces[i]] != " "){
val = val.substring(0, phoneSpaces[i]) + " " + val.substring(phoneSpaces[i], val.length);
}
}
return val
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="inputValue"/>
<label style="float: right;">
Prop Value: <span>{{valueProp}}</span>
</label>
<br>
<label >format (xx xx xx xx xx)</label>
</div>
<template>
<input
@input="formatValue"
type="text"
:value="formattedValue"
/>
</template>
<script type="text/javascript">
import {formatPhoneNumber} from '~/utils/string';
export default {
computed: {
formattedValue: function(){
return formatPhoneNumber(this.value)
},
},
methods: {
formatValue(e) {
this.$emit('input', formatPhoneNumber(e.target.value))
}
},
props: ['value']
}
</script>
只要 formatPhoneNumber(value)
产生不同的值,一切正常,但一旦达到最大长度(自 formatPhoneNumber('xx xx xx xx xx whatever') == 'xx xx xx xx xx'
起),发出的值与当前存储相同一.
完全没问题,只是结果状态没有发生变化,组件也没有重新渲染,因此 formattedValue()
没有被调用。
所以我最终在商店中得到 xx xx xx xx xx
,但输入显示 xx xx xx xx xx whatever
,因为本地输入值与商店不同。
如何避免这种意外行为?将 formatPhoneNumber()
移动到商店并不能解决我的问题,因为它仍然可以防止突变,并且仅在 formattedValue()
中使用 formatPhoneNumber()
会使我在商店中得到一个未格式化的值也不是我想要的。
为什么带有动态 value
集的 Vue input
仍然管理本地状态?
为了实现你想要的(我认为),你可以将 formatValue 方法更改为
formatValue(e) {
this.$emit('input', e.target.value = formatPhoneNumber(e.target.value));
}
以便它将输入设置为格式化的 phone 数值。您将以一种或另一种方式覆盖输入产生的内容,因此您最好在输入事件上执行此操作。
我会使用 v-model 而不是 v 值,因为这样可以让我完全控制要在输入字段中显示的内容。
通过这种方式,您可以格式化输入值,然后将其设置回模型中。它看起来像这样:
<template>
<input @input="formatValue" type="text" v-model="inputModel">
</template>
<script type="text/javascript">
export default {
data() {
return {
inputModel: this.value
};
},
methods: {
formatValue() {
this.inputModel = formatPhoneNumber(this.inputModel);
this.$emit("input", this.inputModel);
}
},
props: ["value"]
};
</script>
这是我创建的 working example 来测试它。
我认为最简单的方法是对父级的@input 事件进行简单的一行修改,在更新它之前清除 prop 值。
你仍然只需要发出一个值,但在使用发出的值之前,清除 prop。
我在下面提供了一个片段(但请注意片段中的其他差异):
我没有指定输入字段值,而是选择使用 v-model 将其绑定到具有 get 和 set 方法的计算 属性。这允许我在访问和修改数据时使用不同的逻辑(在许多情况下非常方便)。
通过分离此逻辑,我能够将功能从输入事件内部移至 set 方法,并完全消除输入事件。
new Vue({
el: "#app",
// props: ['valueProp'],
data: {
valueProp: "" //simulate prop data
},
computed: {
// --Value input element is binded to--
inputValue:{
get(){ //when getting the value, return the prop
return this.valueProp;
},
set(val){ //when the value is set, emit value
this.formatValue(val);
}
}
},
methods: {
// --Emit the value to the parent--
formatValue(val) {
this.parentFunction(this.formatPhoneNumber(val)); //simulate emitting the value
// this.$emit('input', formatPhoneNumber(val));
},
// --Simulate parent receiving emit event--
parentFunction(emittedValue){
console.log("emitted:" + emittedValue);
this.valueProp = null; //first clear it (updates the input field)
this.valueProp = emittedValue; //then assign it the emitted value
},
// --Simulate your format method--
// THIS LOGIC CAN BE IGNORED. It is just a quick implementation of a naive formatter.
// The "important" thing is it limits the length, to demonstrate exceeding the limit doesn't get reflected in the input field
formatPhoneNumber(val){
var phoneSpaces = [2,4,6,8]; //specify space formatting (space locations)
var maxLength = 10; //specify the max length
val = val.replace(/ /g,''); //remove existing formatting
if(val.length > maxLength) //limits the length to the max length
val = val.substring(0, maxLength);
// for the number of desired spaces, check each space location (working backwards) ... if value is longer than space location and space location is not a space ... add a space at the location.
for(var i = phoneSpaces.length-1; i >= 0; i--){
if(val.length > phoneSpaces[i] && val[phoneSpaces[i]] != " "){
val = val.substring(0, phoneSpaces[i]) + " " + val.substring(phoneSpaces[i], val.length);
}
}
return val
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="inputValue"/>
<label style="float: right;">
Prop Value: <span>{{valueProp}}</span>
</label>
<br>
<label >format (xx xx xx xx xx)</label>
</div>