繁体   English   中英

vuejs 从子组件更新父数据

[英]vuejs update parent data from child component

我开始玩 vuejs (2.0)。 我构建了一个包含一个组件的简单页面。 该页面有一个带有数据的 Vue 实例。 在该页面上,我注册并将组件添加到 html。 该组件有一个input[type=text] 我希望该值反映在父级(主 Vue 实例)上。

如何正确更新组件的父数据? 从父级传递绑定的道具并不好,并向控制台抛出一些警告。 他们的文档中有一些东西,但它不起作用。

双向绑定在 Vue 2.0 中已被弃用,以支持使用更多事件驱动的架构。 一般来说,孩子不应该改变它的道具。 相反,它应该$emit事件并让父级响应这些事件。

在您的特定情况下,您可以使用带有v-model的自定义组件。 这是一种特殊的语法,允许接近双向绑定,但实际上是上述事件驱动架构的简写。 你可以在这里阅读 -> https://v2.vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events

这是一个简单的例子:

 Vue.component('child', { template: '#child', //The child has a prop named 'value'. v-model will automatically bind to this prop props: ['value'], methods: { updateValue: function (value) { this.$emit('input', value); } } }); new Vue({ el: '#app', data: { parentValue: 'hello' } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script> <div id="app"> <p>Parent value: {{parentValue}}</p> <child v-model="parentValue"></child> </div> <template id="child"> <input type="text" v-bind:value="value" v-on:input="updateValue($event.target.value)"> </template>


文档指出

<custom-input v-bind:value="something" v-on:input="something = arguments[0]"></custom-input>

相当于

<custom-input v-model="something"></custom-input>

这就是为什么孩子身上的道具需要命名为 value,以及为什么孩子需要 $emit 一个名为input的事件。

在子组件中:

this.$emit('eventname', this.variable)

在父组件中:

<component @eventname="updateparent"></component>

methods: {
    updateparent(variable) {
        this.parentvariable = variable
    }
}

文档中:

在 Vue.js 中,父子组件的关系可以概括为 props down,events up。 父级通过 props 向下传递数据给子级,子级通过事件向父级发送消息。 让我们看看他们接下来是如何工作的。

在此处输入图像描述

如何传递道具

以下是将 props 传递给子元素的代码:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>

如何发出事件

HTML:

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

JS:

Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})

子组件

使用this.$emit('event_name')将事件发送到父组件。

在此处输入图像描述

父组件

为了在父组件中侦听该事件,我们执行v-on:event_name并且我们想要在该事件上执行的方法( ex. handleChange )发生

在此处输入图像描述

完毕 :)

我同意上述事件发射和 v-model 的答案。 但是,我想我会发布我发现的关于具有多个表单元素的组件的内容,这些表单元素想要回传给它们的父级,因为这似乎是谷歌返回的第一批文章之一。

我知道这个问题指定了一个输入,但这似乎是最接近的匹配,并且可能会使用类似的 vue 组件为人们节省一些时间。 此外,还没有人提到.sync修饰符。

据我所知, v-model解决方案只适合一个输入返回其父级。 我花了一些时间寻找它,但 Vue (2.3.0) 文档确实显示了如何将发送到组件的多个道具同步回父级(当然是通过发射)。

它被恰当地称为.sync修饰符。

这是文档中的内容:

在某些情况下,我们可能需要对 prop 进行“双向绑定”。 不幸的是,真正的双向绑定会产生维护问题,因为子组件可以使父组件发生变异,而该变异的来源在父组件和子组件中都很明显。

这就是为什么我们建议以update:myPropName的模式发出事件。 例如,在一个带有title属性的假设组件中,我们可以通过以下方式传达分配新值的意图:

this.$emit('update:title', newTitle)

然后,如果需要,父级可以监听该事件并更新本地数据属性。 例如:

<text-document   
 v-bind:title="doc.title"  
 v-on:update:title="doc.title = $event"
></text-document>

为方便起见,我们使用 .sync 修饰符为这种模式提供简写:

<text-document v-bind:title.sync="doc.title"></text-document>

您还可以通过对象发送一次同步多个。 此处查看文档

更简单的方法是使用this.$emit

父亲.vue

<template>
  <div>
    <h1>{{ message }}</h1>
    <child v-on:listenerChild="listenerChild"/>
  </div>
</template>

<script>
import Child from "./Child";
export default {
  name: "Father",
  data() {
    return {
      message: "Where are you, my Child?"
    };
  },
  components: {
    Child
  },
  methods: {
    listenerChild(reply) {
      this.message = reply;
    }
  }
};
</script>

孩子.vue

<template>
  <div>
    <button @click="replyDaddy">Reply Daddy</button>
  </div>
</template>

<script>
export default {
  name: "Child",
  methods: {
    replyDaddy() {
      this.$emit("listenerChild", "I'm here my Daddy!");
    }
  }
};
</script>

我的完整示例: https ://codesandbox.io/s/update-parent-property-ufj4b

也可以将道具作为对象或数组传递。 在这种情况下,数据将被双向绑定:

(这在主题末尾注明:https ://v2.vuejs.org/v2/guide/components.html#One-Way-Data-Flow )

 Vue.component('child', { template: '#child', props: {post: Object}, methods: { updateValue: function () { this.$emit('changed'); } } }); new Vue({ el: '#app', data: { post: {msg: 'hello'}, changed: false }, methods: { saveChanges() { this.changed = true; } } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script> <div id="app"> <p>Parent value: {{post.msg}}</p> <p v-if="changed == true">Parent msg: Data been changed - received signal from child!</p> <child :post="post" v-on:changed="saveChanges"></child> </div> <template id="child"> <input type="text" v-model="post.msg" v-on:input="updateValue()"> </template>

在父组件中 -->

 data : function(){ return { siteEntered : false, }; },

在子组件中 -->

this.$parent.$data.siteEntered = true;

2021 年答案 - Vue 2.3+

简短回答:只需在父级中添加.sync修饰符并将数据作为道具传递给子级:

    // PARENT:
    data () {
    return {
      formData: {
        members: [] //<- we wanna pass this one down to children and add/remove from the child component
      }
    }

   // PARENT TEMPLATE:
   <!-- ADD MEMBERS -->
  <add-members :members.sync="formData.members" />

嵌套子组件:AddMembers.vue

export default {
  name: 'AddMembers',
  props: ['members'],
  methods: {
    addMember () {
      this.members.push(new Member()) // <-- you can play and reactivity will work (in the parent)  
    },
    removeMember (index) {
      console.log('remove', index, this.members.length < 1)
      this.members.splice(index, 1)
    }
  }
}

长话短说:实际上子组件的更改正在被 $emitted 并更新父组件的formData.members[]

资料来源: Mauro Perez 中等

在孩子

 <input
            type="number"
            class="form-control"
            id="phoneNumber"
            placeholder
            v-model="contact_number"
            v-on:input="(event) => this.$emit('phoneNumber', event.target.value)"
    />

data(){
    return {
      contact_number : this.contact_number_props
    }
  },
  props : ['contact_number_props']

在父母

<contact-component v-on:phoneNumber="eventPhoneNumber" :contact_number_props="contact_number"></contact-component>


 methods : {
     eventPhoneNumber (value) {
      this.contact_number = value
    }

正确的方法是在主 Vue 实例监听的子组件中$emit()一个事件

// Child.js
Vue.component('child', {
  methods: {
    notifyParent: function() {
      this.$emit('my-event', 42);
    }
  }
});

// Parent.js
Vue.component('parent', {
  template: '<child v-on:my-event="onEvent($event)"></child>',
  methods: {
    onEvent: function(ev) {
      v; // 42
    }
  }
});

当我们想要将数据传递给父组件以及当前子组件的另一个嵌套子组件时, using a data property将很有用,如下例所示。

示例:像这样从父组件调用您的子组件。

父组件:

<template>
  <TodoItem :todoParent="todo" />
</template>

<script>
export default {
  data() {
    return {
      todo: {
        id:1,
        task:'todo 1',
        completed:false
      }
    };
  }
}
</script>

子组件:

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todoParent"],
  data() {
    return {
      todo: this.todoParent,
    };
  },
  methods: {
    markCompleted() {
      this.todo.completed = true
    },
  },
};
</script>

即使您可以将此属性传递给嵌套的子组件,它也不会给出此错误/警告。

当您只需要在父组件和子组件之间同步此属性时的其他用例。 可以使用 Vue 的sync修饰符来实现。 v-model也很有用。 此问题线程中提供了许多其他示例。

Example2 :使用组件事件 我们可以从子组件emit事件,如下所示。

父组件:

<template>
  <TodoItem :todo="todo" @markCompletedParent="markCompleted" />
</template>

<script>
export default {
  data() {
    return {
      todo: {
        id:1,
        task:'todo 1',
        completed:false
      }
    };
  },
  methods: {
    markCompleted() {
      this.todo.completed = true
    },
  }
}
</script>

子组件:

<template>
  <div class="todo-item" v-bind:class="{'is-completed':todo.completed}">
    <p>
      <input type="checkbox" @change="markCompleted" />
      {{todo.task}}
      <button class="del">x</button>
    </p>
  </div>
</template>

<script>
export default {
  name: "TodoItem",
  props: ["todo"],
  methods: {
    markCompleted() {
      this.$emit('markCompletedParent', true)
    },
  }
};
</script>

另一种方法是将 setter 的引用从父组件作为道具传递给子组件,类似于他们在 React 中的做法。 比如说,你有一个方法updateValue在父级上更新值,你可以像这样实例化子组件: <child :updateValue="updateValue"></child> 然后在孩子身上你会有一个对应的道具: props: {updateValue: Function} ,并且在模板中当输入改变时调用方法: <input @input="updateValue($event.target.value)">

我不知道为什么,但我刚刚成功地使用数据作为对象更新了父数据, :set & computed

父.vue

<!-- check inventory status - component -->
    <CheckInventory :inventory="inventory"></CheckInventory>

data() {
            return {
                inventory: {
                    status: null
                },
            }
        },

孩子.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

                this.inventory.status = "Out of stock";
                return this.inventory.status;

            },
        }

他的示例将告诉您如何在提交按钮上将输入值传递给父级。

首先将 eventBus 定义为新的 Vue。

//main.js
import Vue from 'vue';
export const eventBus = new Vue();

Pass your input value via Emit.
//Sender Page
import { eventBus } from "../main";
methods: {
//passing data via eventbus
    resetSegmentbtn: function(InputValue) {
        eventBus.$emit("resetAllSegment", InputValue);
    }
}

//Receiver Page
import { eventBus } from "../main";

created() {
     eventBus.$on("resetAllSegment", data => {
         console.log(data);//fetching data
    });
}

我认为这可以解决问题:

@change="$emit(variable)"

介绍

我正在寻找在 vue3 中从父级向子级(以及返回)发送数据(我知道问题是关于 vue2,但当时在 SO 上没有对 vue3 的引用)。

下面是工作样板结果,纯“html + js”,没有打包程序、模块等,我有一些警告,解释说。

笔记:

  1. 插入子行
    <component-a :foo="bar" @newfooevent="bar = $event"></component-a>`
  • 我使用简写将parent.bar绑定到child.foo :foo="bar" ,与v-bind:foo="bar"相同。 它通过 props 将数据从父级传递给子级。

  • 警告:事件监听器应该只放在子组件标签中!

    那是@newfooevent="bar = $event"部分。

    您无法在<div id="app">或父级内部的任何其他位置捕获信号。

    尽管如此,这是宇宙中父母的一面,在这里您可以访问所有父母的数据并从孩子的信号中提取数据来处理它。

  1. 您可以创建应用程序,并在它之后定义组件( app.component("component-a", ...)部分。

    警告:不需要前向声明组件,例如 C/C++ 中的函数。 您可以创建使用该组件的应用程序,然后定义该组件。 我花了很多时间寻找以某种方式声明它的方法——没必要。

  2. 在这里,您可以找到v-model用法的一个很好的示例,以及我用来解决问题的代码: https ://javascript.plainenglish.io/vue-3-custom-events-d2f310fe34c9

这个例子

 <!DOCTYPE html> <html lang="en"> <head> <title>App</title> <meta charset="utf-8" /> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <component-a :foo="bar" @newfooevent="bar = $event"></component-a> <p>Parent copy of `bar`: {{ bar }}</p> <button @click="bar=''">Clear</button> </div> <script> const app = Vue.createApp({ data() { return { bar: "bar start value" }; } }); app.component("component-a", { props: { foo: String }, template: ` <input type="text" :value="foo" @input="$emit('newfooevent', $event.target.value)"> ` }); app.mount("#app"); </script> </body> </html>

还有另一种从子节点到父节点的通信数据更改的方法,它使用provide - inject方法。 父组件为子组件“提供”数据或方法,然后将该数据或方法“注入”到子组件中——但它也可用于触发父组件中的方法并传递参数。
当有一个恰好嵌入到多个其他组件中的子组件时,这种方法特别有用。 此外,在大型项目中必须注意不要失去对provideinject使用的概述。

父(顶级)组件App.vue使用provide来访问其方法updateParentValue的示例(如果提供了方法而不是数据,则provide方法的形式):

<template>
  <h2>App.vue, parentValue is: <em>{{ parentValue }}</em></h2>
  <ChildComponent1 />
</template>

<script>
import ChildComponent1 from "./components/ChildComponent1.vue";

export default {
  data() {
    return {
      parentValue: "",
    };
  },
  components: {
    ChildComponent1,
  },
  provide() {
    return {
      updateParent: this.updateParentValue,
    };
  },
  methods: {
    updateParentValue($value) {
      this.parentValue = $value;
    },
  },
};
</script>

在这个示例中,组件Component4.vue位于“底部”,即 App.vue 包含 Component1,Component1 包含 Component2... 直到 Component4 实际使用inject来访问父方法,然后调用该方法和参数$value通过(这里只是一个随机数):

<template>
  <div>
    <h2>ChildComponent4.vue</h2>
    <button @click="updateParent(Math.random())">
      Update parent value in App.vue
    </button>
  </div>
</template>

<script>
export default {
  inject: ["updateParent"],
};
</script>

整个例子都可以在这里找到
Vue.js 文档

在此处输入图像描述

发出事件并使用“emits”属性将其注册到子组件中。 在您创建子实例的父元素处侦听发出的事件,并将事件侦听器指向您要执行的函数。

例如。

家长

<template>
   <child @close="closeIt"></child>
</template>

<script>
   export default {
      methods: {
         closeIt() {
            //DO SOMETHING...
         }
      }
   }
</script>

孩子

<template>
   <button @click="closeInParent">Close</button>
</template>

<script>
   export default {
      emits: ["close"],
      methods: {
         closeInParent() {
             this.$emit('close')
         }
      }
   }
</script>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM