【问题标题】:How to defer form input binding until user clicks the submit button?如何推迟表单输入绑定,直到用户单击提交按钮?
【发布时间】:2017-12-16 22:16:04
【问题描述】:

我想在 Vue.js 2.3 中对我的表单输入进行双向数据绑定。但是,我不能使用v-model 指令,因为我希望仅在单击提交按钮时更新数据。同时,输入值可能是从另一个 Vue 方法更新的,所以它应该绑定到数据属性text。我编了这样的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;
    }
  }
});

它可以工作,但是当有更多输入时它不能很好地扩展。有没有更简单的方法来实现这一点,而不使用 $refs?

【问题讨论】:

    标签: vue.js vuejs2


    【解决方案1】:

    您可以使用一个对象并将其属性绑定到输入。然后,在您的 update 方法中,您可以将属性复制到另一个对象以进行显示。然后,您可以设置一个深度观察器,以便在该对象更改时更新输入的值。复制属性时需要使用this.$set,以便将更改注册到 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>

    如果你使用的是 ES2015,你可以直接复制对象,所以这不是那么冗长:

    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>

    【讨论】:

    • 谢谢,但与@DecadeMoon 的问题相同:如果我想从另一种方法更改displayVals 怎么办?输入字段不会自动更新。如何将displayVals 绑定到inputVals 与比例?
    • 你的意思是你想在调用update之后将displayVals永久绑定到inputVals
    • 我希望“事实来源”为displayVals,我想修改它不仅通过提交表单,还通过其他方法。但后来我希望更新后的displayVals 自动显示在输入中,用户可以编辑它们,只有在单击提交按钮后才应该更新它们。如果在每次击键时更新值,这将是 v-model 的一个简单用例,而我的情况并非如此。
    • 我更新了我的答案。也许您可以更新您的问题以明确该要求?
    • 谢谢,我更新了问题。但是,这比使用$refs 更冗长。我认为应该有一个像v-model.submit 这样的修饰符,它只会在表单提交时更新值(就像v-model.lazy 在更改时所做的那样)。
    【解决方案2】:

    您可以使用两个单独的数据属性,一个用于&lt;input&gt; 的值,另一个用于单击“确定”按钮后提交的值。

    <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

    【讨论】:

    • 谢谢,但是如果我想从其他方法更改text 值,输入字段将不会更新。如何将text 绑定到editText
    • 使用观察者观察text的变化并将editText设置为text的新值。
    • 如果我有 10 个输入怎么办?这比使用$refs 更多样板。没有更简单的方法吗?
    • 每个输入字段仍然需要两个数据属性。有一个项目数组,其中每个项目都是一个具有两个属性的对象:editTexttext
    【解决方案3】:

    使用与其他答案略有不同的方法,我认为您可以实现易于扩展的目标。

    这是第一次,但使用组件,您可以构建自己的输入元素,并在您需要的时候精确提交。下面是一个输入元素的示例,当它在 t-form 组件之外时,它的工作方式与常规输入元素类似,但在 submit 内时仅在 submit 上更新 v-model

    Vue.component("t-input", {
      props:["value"],
      template:`
        <input type="text" v-model="internalValue" @input="onInput">
      `,
      data(){
        return {
          internalValue: this.value,
          wrapped: false
        }
      },
      watch:{
        value(newVal){
          this.internalValue = newVal
        }
      },
      methods:{
        update(){
          this.$emit('input', this.internalValue)
        },
        onInput(){
          if (!this.wrapped)
            this.$emit('input', this.internalValue)
        }
      },
      mounted(){
        if(this.$parent.isTriggeredForm){
          this.$parent.register(this)
          this.wrapped = true      
        }
      }
    })
    

    这是t-form 的示例。

    Vue.component("t-form",{
      template:`
        <form @submit.prevent="submit">
          <slot></slot>
        </form>
      `,
      data(){
        return {
          isTriggeredForm: true,
          inputs:[]
        }
      },
      methods:{
        submit(){
          for(let input of this.inputs)
            input.update()
        },
        register(input){
          this.inputs.push(input)
        }
      }
    })
    

    有了这些,你的工作就变得非常简单了。

    <t-form>
      <t-input v-model="text"></t-input><br>
      <t-input v-model="text2"></t-input><br>
      <t-input v-model="text3"></t-input><br>
      <t-input v-model="text4"></t-input><br>
      <button>Submit</button>
    </t-form>
    

    此模板只会在单击按钮时更新绑定的表达式。您可以拥有任意数量的t-inputs

    这是一个工作示例。我在表单内部和外部都包含了t-input 元素,因此您可以看到在表单内部,模型仅在提交时更新,而在表单外部,这些元素就像一个典型的输入一样工作。

    console.clear()
    //
    Vue.component("t-input", {
      props: ["value"],
      template: `
        <input type="text" v-model="internalValue" @input="onInput">
      `,
      data() {
        return {
          internalValue: this.value,
          wrapped: false
        }
      },
      watch: {
        value(newVal) {
          this.internalValue = newVal
        }
      },
      methods: {
        update() {
          this.$emit('input', this.internalValue)
        },
        onInput() {
          if (!this.wrapped)
            this.$emit('input', this.internalValue)
        }
      },
      mounted() {
        if (this.$parent.isTriggeredForm) {
          this.$parent.register(this)
          this.wrapped = true
        }
      }
    })
    
    Vue.component("t-form", {
      template: `
        <form @submit.prevent="submit">
          <slot></slot>
        </form>
      `,
      data() {
        return {
          isTriggeredForm: true,
          inputs: []
        }
      },
      methods: {
        submit() {
          for (let input of this.inputs)
            input.update()
        },
        register(input) {
          this.inputs.push(input)
        }
      }
    })
    
    
    new Vue({
      el: "#app",
      data: {
        text: "bob",
        text2: "mary",
        text3: "jane",
        text4: "billy"
      },
    })
    <script src="https://unpkg.com/vue@2.2.6/dist/vue.js"></script>
    <div id="app">
      <t-form>
        <t-input v-model="text"></t-input><br>
        <t-input v-model="text2"></t-input><br>
        <t-input v-model="text3"></t-input><br>
        <t-input v-model="text4"></t-input><br>
        <button>Submit</button>
      </t-form>
      Non-wrapped:
      <t-input v-model="text"></t-input>
      <h4>Data</h4>
      {{$data}}
      <h4>Update Data</h4>
      <button type="button" @click="text='jerome'">Change Text</button>
    </div>

    【讨论】:

      猜你喜欢
      • 2021-09-29
      • 1970-01-01
      • 2017-08-15
      • 2013-08-09
      • 1970-01-01
      • 1970-01-01
      • 2021-08-12
      • 1970-01-01
      • 2012-08-07
      相关资源
      最近更新 更多