【问题标题】:Why won't v-model of a subcomponent reflow from an emit changed to the parent?为什么子组件的 v-model 不会从发射更改为父组件?
【发布时间】:2021-02-15 11:18:40
【问题描述】:

我正在为我的 VueJS 应用程序创建一个数字文本输入,它将允许在文本字段中评估 math.js 表达式。这个想法是,在模糊或在文本框中按 Enter 时,如果可能,将对表达式进行评估。

<template>
  <v-text-field
    v-model="localValue"
    @blur="onBlur"
    @keydown="onKeydown"
  />
</template>

<script>
import { create, all } from 'mathjs'
const math = create(all)

export default {
    name: "NumericTextField",
    props: ["value"],
    data () {
        return {
            localValue: this.value
        }
    },
    methods: {
        onKeydown (e) {
            if (e.key === "Enter") {
                this.compute()
            }
        },
        onBlur () {
            this.compute()
        },
        compute () {
            try {
                const result = math.evaluate(this.localValue)
                this.$emit("input", result)
            } catch (e) {
                console.log(e)
            }
        },
    },
}
</script>

我看到数据通过 emit 语句传播出去。但是,新值一旦计算出来,就不会回流到文本字段的 v-model 中。我已经在我的数据函数上尝试了一个 console.log,并且在发射完成后我实际上看到了新的 this.value。因此,如果我在字段中键入 1+1 并按 Enter,我将看到发射显示 2 的 console.log,并且重新计算的数据也显示 this.value 现在是 2。但是,视觉输出是 v-text-field 的数据没有变化,文本框中仍然显示1+1。这是为什么呢?

【问题讨论】:

    标签: vue.js vuetify.js


    【解决方案1】:

    有两种方法可以实现这一点。

    选项 1 - 将 v-model 绑定到局部变量,但观察 prop 值的变化并在 prop 变化时更新局部变量:

    <template>
      <v-text-field v-model="localValue" @blur="compute" @keydown.enter="compute" />
    </template>
    
    <script>
    import { create, all } from "mathjs";
    const math = create(all);
    
    export default {
      name: "NumericTextField",
      props: ["value"],
      data() {
        return {
          localValue: this.value
        };
      },
      watch: {
        value: function(newVal) {
          this.localValue = newVal;
        }
      },
      methods: {
        compute() {
          try {
            const result = math.evaluate(this.localValue);
            this.$emit("input", result);
          } catch (e) {
            console.log(e);
          }
        }
      }
    };
    </script>
    

    选项 2 - 使用 value 作为输入值,但在单独的局部变量中跟踪 v-text-field 的变化。这样你就不需要使用手表了。

    <template>
      <v-text-field :value="value" @blur="compute" @keydown.enter="compute" @input="onInput" />
    </template>
    
    <script>
    import { create, all } from "mathjs";
    const math = create(all);
    
    export default {
      name: "NumericTextField",
      props: ["value"],
      data() {
        return {
          localValue: this.value
        };
      },
      methods: {
        compute() {
          try {
            const result = math.evaluate(this.localValue);
            this.$emit("input", result);
          } catch (e) {
            console.log(e);
          }
        },
        onInput(val) {
          this.localValue = val;
        }
      }
    };
    </script>
    

    【讨论】:

    • 我们在同一时间考虑相同的解决方案 :) codesandbox.io/s/mathjs-vue-5e6m9?file=/src/…
    • 我不明白如何在这里使用 onInput 分配 this.localValue = val 然后将 v-text-field 的值绑定到 this.localValue 与我之前所做的不同使用 v-model="this.localValue"... 不是等效的吗?
    • 在选项 2 中,您没有将 :value 绑定到 localValue,而是将其绑定到 prop value,即 :value="value",这是父模型的值。但是随后您通过 onInput 在局部变量上跟踪对 v-text-field 所做的更改,并且只有在您决定在 blur 或 enter 上发出更改时才更新父模型(即 prop 值)。一旦发出,父模型值就会改变,从而改变这个组件值 prop,并且由于 prop 绑定到 v-text-field 值,它将被更新。
    • 啊,我明白了。谢谢。
    【解决方案2】:

    这是因为通过使用 v-model,你改变了 localValue,它不再等于 this.value。您应该尝试完全删除 localValue,并改用 this.value。

    <v-text-field
        v-model="value"
        @blur="compute"
        @keydown.enter="compute"
    />
    
    compute () {
       try {
           const result = math.evaluate(this.value)
              this.$emit("input", result)
                } catch (e) {
                    console.log(e)
                }
            },
    
    
    

    【讨论】:

    • 你必须绑定 :value="value" 否则你最终会改变组件的 prop 值,Vue 会抱怨。
    • 如果 this.value 永远不会改变,除非它是发射输入的结果,那么 this.value 什么时候会等于 "1+1" ?在这里,您使用 this.value 作为评估的输入,但是 this.value 不应该被修改,所以这就是我使用 localValue 来表示字符串表达式的原因。如果我使用@beyers 解决方法,文本也不会重排。
    【解决方案3】:

    尝试使用value 属性将文本字段直接绑定到value 属性并删除localValue

    <template>
      <v-text-field
        :value="value"
        @blur="compute"
        @keydown.enter="compute"
      />
    </template>
    
    <script>
    import { create, all } from 'mathjs'
    const math = create(all)
    
    export default {
        name: "NumericTextField",
        props: ["value"],
        methods: {
            compute () {
                try {
                    const result = math.evaluate(this.value)
                    this.$emit("input", result)
                } catch (e) {
                    console.log(e)
                }
            },
        },
    }
    </script>
    

    如果上面的例子不起作用,试试下面的

    <template>
      <v-text-field v-model="localValue" @blur="compute" @keydown.enter="compute"
     
      />
    </template>
    
    <script>
    import { create, all } from "mathjs";
    const math = create(all);
    
    export default {
      name: "NumericTextField",
      props: ["value"],
      data() {
        return { localValue: "" };
      },
      watch: {
        value(v) {
          this.localValue = v;
        },
      },
      methods: {
        compute() {
     
          try {
            const result = math.evaluate(this.localValue);
            this.$emit("input", result);
          } catch (e) {
            console.log(e);
          }
        },
      },
    };
    </script>
    

    LIVE DEMO

    【讨论】:

    • 当我这样做时,我收到一个关于“避免直接改变道具”的错误。
    • 它应该根据this工作
    • 尝试使用空处理程序添加@input
    • 当我使用这个确切的代码时,我看到了一个错误。此外,如果您不修改值,让 v-text-field 的 v-model 将其更改为类似于我在 localValue 中所做的“1+1”,那么 this.value 如何包含某个任意路径的值表达式为字符串?我想我需要一些当地的状态。也许我错了,但我在您的答案中还没有找到解决方法。
    猜你喜欢
    • 2023-02-03
    • 2021-06-16
    • 2019-02-14
    • 2018-02-21
    • 1970-01-01
    • 2018-11-15
    • 2018-09-06
    • 2022-08-20
    • 2018-04-28
    相关资源
    最近更新 更多