簡體   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