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>