Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to defer form input binding until user clicks the submit button?

Tags:

vue.js

vuejs2

I wanted to make a two-way data binding on my form input in Vue.js 2.3. However, I cannot use the v-model directive, because I want the data to be updated only on clicking the submit button. Meanwhile, the input value may be updated from another Vue method, so it should be bound to the data property text. I made up something like this jsFiddle:

<div id="demo">
  <input :value="text" ref="input">
  <button @click="update">OK</button>
  <p id="result">{{text}}</p>
</div>
new Vue({
  el: '#demo',
  data: function() {
    return {
      text: ''
    };
  },
  methods: {
    update: function () {
        this.text = this.$refs.input.value;
    }
  }
});

It works, but it does not scale well when there are more inputs. Is there a simpler way to accomplish this, without using $refs?

like image 877
niutech Avatar asked Jul 12 '17 13:07

niutech


2 Answers

You can use an object and bind its properties to the inputs. Then, in your update method, you can copy the properties over to another object for display purposes. Then, you can set a deep watcher to update the values for the inputs whenever that object changes. You'll need to use this.$set when copying the properties so that the change will register with Vue.

new Vue({
  el: '#demo',
  data: function() {
    return {
      inputVals: {
        text: '',
        number: 0
      },
      displayVals: {}
    };
  },
  methods: {
    update() {
      this.copyObject(this.displayVals, this.inputVals);
    },
    copyObject(toSet, toGet) {
      Object.keys(toGet).forEach((key) => {
        this.$set(toSet, key, toGet[key]);
      });
    }
  },
  watch: {
    displayVals: {
      deep: true,
      handler() {
        this.copyObject(this.inputVals, this.displayVals);
      }
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
  <input v-model="inputVals.text">
  <input v-model="inputVals.number">
  <button @click="update">OK</button>
  <input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>

If you're using ES2015, you can copy objects directly, so this isn't as verbose:

new Vue({
  el: '#demo',
  data() {
    return {
      inputVals: { text: '', number: 0 },
      displayVals: {}
    };
  },
  methods: {
    update() {
      this.displayVals = {...this.inputVals};
    },
  },
  watch: {
    displayVals: {
      deep: true,
      handler() {
        this.inputVals = {...this.displayVals};
      }
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="demo">
  <input v-model="inputVals.text">
  <input v-model="inputVals.number">
  <button @click="update">OK</button>
  <input v-for="val, key in displayVals" v-model="displayVals[key]">
</div>
like image 77
thanksd Avatar answered Sep 30 '22 05:09

thanksd


You can use two separate data properties, one for the <input>'s value, the other for the committed value after the OK button is clicked.

<div id="demo">
  <input v-model="editText">
  <button @click="update">OK</button>
  <p id="result">{{text}}</p>
</div>
new Vue({
  el: '#demo',
  data: function() {
    return {
      editText: '',
      text: ''
    };
  },
  methods: {
    update: function () {
      this.text = this.editText;
    }
  }
});

Updated fiddle

like image 42
Decade Moon Avatar answered Sep 30 '22 05:09

Decade Moon