【问题标题】:Syncing an object property with child components in Vue在 Vue 中将对象属性与子组件同步
【发布时间】:2019-12-01 20:56:09
【问题描述】:

背景

我正在使用 Typescript 3.5.3 中基于类的组件在 Vue.js 2.6.10 中构建一个包含大量输入的大型表单。 (在下面的代码 sn-ps 中,我已将其转换为 JS,因为我确信 TS 不是问题。)为了不让用户不知所措,表单被分解为多个组件;一次只向用户显示一个。

父组件

<template>
    <form>
        <Basic :Container.sync="Container"/>
        <!-- More custom components -->
        <output>{{Container.name}}</output>
    </form>
</template>

Container 对象是要收集的所有数据的包装器。您会看到我将整个对象传递给子组件,而不是只传递请求给子组件的属性。这似乎是固执己见,但我有我的理由。

Form 组件归结为:

// Imports left out for brevity
@Component({ components: {  Basic, }, })
export default class Form extends Vue {
    private Container
    async created() {
        this.Container = {
            name: null
        }
        setInterval(()=>{console.log(this.Container.name)}, 5000)
        // Logs whatever will be entered in the input in the Basic component
    }
}

子组件

引用的组件Basic 是一个普通的输入元素:

<template>
    <input v-model="Container.name" @input="emitUpdate">
</template>

使用同样简单的组件定义:

// Imports left out for brevity
@Component({})
export default class Basic extends Vue {
    @Prop() Container
    emitUpdate() {
        console.log(this.Container.name) // Logs whatever was entered in the input
        this.$emit('update:container', this.Container)
    }
}

问题

我期望看到的是要更新的容器,以及第一个 HTML sn-p 中的 &lt;output&gt; 以显示当前名称。然而,即使变量发生了变化,它也不会,正如两个控制台日志所证明的那样,它们都正确地注册了输入值。

似乎 Vue 的响应性无法将更改注册到 Container 对象,因此不会重新渲染任何 HTML。我该如何解决这个问题?

尝试的解决方案

我尝试使用&lt;Basic … :Container.sync="Container" @Container="Container = $event" /&gt; 明确监听更新事件,但无济于事。

考虑到 Vue 可能不会对对象属性的变化做出反应(毕竟,观察对象也需要设置 deep 选项),我还尝试单独传递 Container 的属性,导致以下结果变化:

在表单的 html 中object is used as the argument to v-bind(我尝试过使用和不使用@update:name):

<Basic … v-bind.sync="Container" @update:name="name=$event"/>

在子组件中,我已更改为 getter 和 setter,以避开“Thou shall not change Props”警告:

<input v-model="Name" @input="emitUpdate">`
@Prop() name
get Name() {return this.name }
set Name(newName) {this.$emit('update:name', newName)}

【问题讨论】:

  • 我可以使用纯 'JS' 编写解决方案,但不能使用 TS,我应该吗?
  • @EvilArthas 是的,请这样做。问题很可能不是源于打字稿。

标签: vue.js vuejs2 vue-component


【解决方案1】:

在我看来,这里的问题是您在 created 钩子内初始化了 Container 对象,这使得它没有反应。如果你想让一个状态响应,你需要将它初始化为初始数据(或使用Vue.observable API)。我对 TS 了解不多,但看看the docs,我认为你需要做的就是......

@Component({ components: {  Basic, }, })
export default class Form extends Vue {
    Container: object = {
        name: null
    }
}

【讨论】:

  • 确实如此!我的印象是,通过在类中简单地声明Container,它将获得undefined 的初始值,这足以启用反应性。当我实施您建议的更改(或最初将 Container 设置为 null 时,反应性已启用。
【解决方案2】:

由于 Vue 不允许我们改变 props,我们需要创建 Container 的本地副本,所以让我们为其使用 computed 属性。当我们尝试访问这个属性时,我们得到了我们的Component,但是当我们想要设置它时,我们只是触发了我们的父组件的事件,新值为Component,我们将通过props获得。

Reference

<Basic v-model="container" />

// Basic.vue
<input v-model="localContainer.name">

props: {
  value: {
    type: Object,
    required: true
  }
},
computed: {
  localContainer: {
    get() {
      return this.value
    },
    set(container) {
      this.$emit('input', container)
    }
  }
}

【讨论】:

    猜你喜欢
    • 2017-11-18
    • 2020-06-10
    • 2017-11-03
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    • 2018-08-03
    相关资源
    最近更新 更多