如何使用 Vue.js 在父组件中更新子组件中的数据?

Posted

技术标签:

【中文标题】如何使用 Vue.js 在父组件中更新子组件中的数据?【英文标题】:How can I use data in the child component that are updated in parent component using Vue.js? 【发布时间】:2019-02-19 09:02:46 【问题描述】:

我的父组件如下所示:

<template>
    ...
        <PaymentMethod/>
        ...
        <b-btn class="float-right" variant="primary" @click="add">
             OK
        </b-btn>
    ...
</template>
<script>
    export default 
        ...
        methods: 
            add() 
                ...
            
        
    
</script>

我的子组件看起来像:

<template>
    ...
        <b-form-select v-model="selected" :options="methodOptions" />
        ...
        <b-form-select v-model="termSelected" :options="termOptions" />
    ...
</template>
<script>
    export default 
        data () 
            return 
                selected: null,
                termSelected: null
            
        ,
        ...
    
</script>

如果从父组件调用add方法,我希望它获取子组件中选择的数据

我该怎么做?

【问题讨论】:

你发出来,vuejs.org/v2/guide/components.html 【参考方案1】:

一种解决方案是使用 sync 修饰符以及计算的 getter 和 setter:

父组件

<template>
    ...
        <PaymentMethod :method.sync="method" :term.sync="term"/>
        ...
        <b-btn class="float-right" variant="primary" @click="add">
             OK
        </b-btn>
    ...
</template>
<script>
    export default 
        data () 
            return 
                method: null,
                term: null
            
        ,
        ...
    
</script>

子组件

<template>
    ...
        <b-form-select v-model="_method" :options="methodOptions" />
        ...
        <b-form-select v-model="_term" :options="termOptions" />
    ...
</template>
<script>
    export default 
        props: ['method', 'term'],
        computed: 
            _method: 
               get () 
                   return this.method
               ,
               set (value) 
                   this.$emit('update:method', value)
               
            ,
            _term: 
               get () 
                   return this.term
               ,
               set (value) 
                   this.$emit('update:term', value)
               
            ,
        ,
        ...
    
</script>

现在使用父组件的 add 方法,您可以访问子组件的选定 methodterm 选项:

methods: 
    add() 
        // this.method is the value of _method 
        // this.term is the value of _term
    

更新

由于您已声明您需要所选术语/方法的值和文本,我建议进行以下更改:

家长

<template>
    ...
        <PaymentMethod :methods="methods" 
                       :terms="terms"  
                       :method.sync="method" 
                       :term.sync="term"/>
        ...
        <b-btn class="float-right" variant="primary" @click="add">
             OK
        </b-btn>
    ...
</template>
<script>
    export default 
        data () 
            return 
                // define your method and term arrays in the parent component.
                // pass them as props to the child component.
                methods: [..., ..., ...],
                terms: [..., ..., ...],
                method: null,
                term: null
            
        ,
        // use computed properties to retrieve the selected method / term option.
        computed: 
            selectedMethod () 
                return this.methods.find(method => method.value === this.method)
            ,
            selectedTerm () 
                return this.terms.find(term => term.value === this.term)
            ,
        
        ...
    
</script>

儿童

<template>
    ...
        <b-form-select v-model="_method" :options="methods" />
        ...
        <b-form-select v-model="_term" :options="terms" />
    ...
</template>
<script>
    export default 
        props: ['method', 'term', 'methods', 'terms'],
        computed: 
            _method: 
               get () 
                   return this.method
               ,
               set (value) 
                   this.$emit('update:method', value)
               
            ,
            _term: 
               get () 
                   return this.term
               ,
               set (value) 
                   this.$emit('update:term', value)
               
            ,
        ,
        ...
    
</script>

【讨论】:

太棒了。但仍有不足之处。此代码:this.$emit('term', value)。应该this.$emit('update:term', value) 我可以从每个选项中获取文本吗?如果我使用你的方式,我只会得到每个选项的价值。我也想收文 你怎么看? 为此,您需要执行以下操作:1) 使用所选对象触发事件 - this.$emit('selected-term', this.terms.find(term =&gt; term.value === value)) - 而不是 this.$emit('update:term', value),然后手动更新父组件中的道具,或者 2 ),将方法和术语也作为道具传递给子组件,当更新事件触发时,在父组件中搜索相应的术语/方法,如第一个示例所示。两者都相似,但#2 更容易实现,IMO。 Vuex/shared state 也是一个不错的选择。 我尝试更新答案并更改父组件中的数据,如下所示:methods: [], terms: []。在selectedMethod 计算中,我添加console.log(this.methods)。然后我尝试选择方法。没有结果【参考方案2】:

使用 Vuex,您的 store.js 将如下所示:

 import Vue from 'vue';
 import Vuex from 'vuex';
   Vue.use(Vuex);


 const state = 
    selected: null
 ;
 const mutations = 
    SET_SELECTED(state, selected) 
    state.selected =selected;
   
   

 export default new Vuex.Store(
    state,
   mutations
   )

在 app.js 中:

import store from './vuex/store';
const app = new Vue(
el: '#app',
store,
...)

在父组件中:

    <template>
     ...
   <PaymentMethod :selected="selected" />
     ...
        <b-btn class="float-right" variant="primary" @click="add">
        OK
        </b-btn>
     ...
    </template>
  <script>
   export default 
    data()
     returen 
       selected:null    
     
     ,
    methods: 
        add() 
         ...
       this.$store.commit("SET_SELECTED",this.selected) 
      
    
 
 </script>

在子组件中:

   <template>
     ...
     <b-form-select @change="selectItem" v-model="selected" 
             :options="methodOptions" />
   ...
 </template>
<script>
  export default 
   data () 
    return 
      selected:null
    
 ,
 methods:
   selectItem()
    this.$store.commit("SET_SELECTED",this.selected
    
    ...
 

【讨论】:

存在错误:[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "selected" 是的,您可以将v-model 绑定到数据对象中名为selectedProp 的属性 但是有了你的解决方案,他仍然无法更改子组件中的this.selected。当你使用v-model 时,你应该实现getter 和setter。 @boussadjra brahim,它不起作用。如果我在add方法中console.log(this.selectedInParent),结果是null @boussadjra brahim 似乎可行。但是如果我有 2 个选择呢?试着看看我的问题。我已经更新了【参考方案3】:

最好和推荐的方法是使用 $emit 下面的例子可以告诉你怎么做?

Vue.component('child-tmp', 
  props: ['post'],
  template: `
  <button @click="$emit('add-like')">Like</button>
  `
)
new Vue(
  el: "#app",
  data: 
    posts: [
      title: "Heading goes here 1",
      content: "Something sadofnd idfidif disong.",
      likes: 0
    ,
    
      title: "Heading 2 goes here",
      content: "Something sadofnd idfidif disong.",
      likes: 0
    ],
    totalLikes: 0
  
)
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <h2>Posts:</h2>
  <section>
    <div v-for="post in posts">
      <h2>
        post.title
      </h2>
      <p>
        post.content
      </p>
      <child-tmp v-bind:post="post" v-on:add-like="post.likes = post.likes+1"></child-tmp><span style="margin-left:10px;">post.likes</span>
    </div>
  </section>
</div>

在上面的 sn-p 中,您可以看到我们正在使用 props 从父级到子级读取数据,我们使用 $emit 从子级传递数据,并使用 v-on:add-like 接收发出的数据。

【讨论】:

【参考方案4】:

有两种方式:

1.您可以在子组件中选择数据时发出事件:

<template>
    ...
        <b-form-select v-model="selected" :options="methodOptions" />
    ...
</template>
<script>
    export default 
        data () 
            return 
                selected: null
            
        ,
        ...
        methods: 
          onSelect() 
            this.$emit('selectData', this.selected);
          
        
    
</script>

然后在父组件中处理这个事件:

<template>
    ...
        <PaymentMethod @selectData="changeChildData($event)"/>
        ...
        <b-btn class="float-right" variant="primary" @click="add">
             OK
        </b-btn>

    ...
</template>
<script>
    export default 
        ...
        data() 
            return 
              childData: null
            
        ,
        methods: 
            changeChildData(newData) 
              this.childData = newData;
            ,
            add() 
                ...
            
        
    
</script>

2。或者你可以使用存储Vuex

或事件总线(Vuex 的简化版):

import Vue from 'vue';
export const EventBus = new Vue();

您将数据从子级传递到事件总线

<script>
    import  EventBus  from '../EventBus.js';
    export default 
        data () 
            return 
                selected: null
            
        ,
        ...
        methods: 
          onSelect() 
            EventBus.$emit('selectData', this.selected);
          
        
    
</script>

并从父母那里订阅它的变化

<script>
    import  EventBus  from '../EventBus.js';
    export default 
        ...
        data() 
            return 
              childData: null
            
        ,
        created() 
            EventBus.$on('selectData', (data) => 
                this.childData = data;
            );
        ,
        methods: 
            add() 
                ...
            
        
    
</script>

【讨论】:

你的第一种方法是行不通的。如果我在onSelect 方法中console.log('test') 并选择了该选项,则没有结果 @SuccessMan 不理解与console.log() 的联系。它不会以任何方式影响代码。我从我的工作代码中获取了这个片段。也许有些错别字,但它对我有用。 @SuccessMan 我修改了父代码,引入了新方法。尝试测试这个变量并确保调用了 changeChildData 方法。 您的第一个示例正是 sync 修饰符的用途。将 Vuex 等同于事件总线过于简单化,并且忽略了反应状态系统的要点。 @DigitalDrifter 不知道sync。好像是个新功能。谢谢!

以上是关于如何使用 Vue.js 在父组件中更新子组件中的数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何仅在父组件完全挂载时挂载 vue js 子组件-我正在使用 $refs

在父组件的值更改时更新子组件中的值

自定义事件在 Vue.js 组件中的应用

如何在父组件的ajax更新中排除子组件?

Vue.js 中父级数据更改时如何重新渲染内部组件

如何在 Vue JS 中将更新的值从父组件发送到子组件?