Vue + Vuex:如果商店没有变化,则输入值不会由商店设置

时间:2019-12-10 09:21:13

标签: vue.js vuex mutation

<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()移至商店并不能解决我的问题,因为它仍然可以防止突变,而仅在formatPhoneNumber()中使用formattedValue()会使我最终在商店中得到未格式化的值也不是我想要的。

具有动态input集的Vue的value仍然如何管理本地状态?

3 个答案:

答案 0 :(得分:3)

要实现所需的目标(我认为),可以将formatValue方法更改为

formatValue(e) {
   this.$emit('input', e.target.value = formatPhoneNumber(e.target.value));
}

因此它将输入设置为格式化的电话号码值。一种或另一种方式是,您将覆盖输入产生的结果,因此您也可以在输入事件中执行该操作。

答案 1 :(得分:2)

我将使用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进行测试。

答案 2 :(得分:0)

我认为最简单的方法是对父项的@input事件进行简单的单行修改,从而在更新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>