vuejs cập nhật dữ liệu cha mẹ từ thành phần con


154

Tôi đang bắt đầu chơi với vuejs (2.0). Tôi đã xây dựng một trang đơn giản với một thành phần trong đó. Trang này có một ví dụ Vue với dữ liệu. Trên trang đó tôi đã đăng ký và thêm thành phần vào html. Các thành phần có một input[type=text]. Tôi muốn giá trị đó phản ánh trên cha mẹ (ví dụ Vue chính).

Làm cách nào để cập nhật chính xác dữ liệu gốc của thành phần? Vượt qua một chỗ dựa ràng buộc từ cha mẹ là không tốt và ném một số cảnh báo đến bàn điều khiển. Họ có một cái gì đó trong tài liệu của họ nhưng nó không hoạt động.


1
Bạn có thể thêm mã bạn đã thử, không hoạt động.
Saurabh

Câu trả lời:


181

Ràng buộc hai chiều đã không được chấp nhận trong Vue 2.0 để sử dụng kiến ​​trúc hướng sự kiện nhiều hơn. Nói chung, một đứa trẻ không nên đột biến đạo cụ của nó. Thay vào đó, nó nên $emitsự kiện và để phụ huynh trả lời những sự kiện đó.

Trong trường hợp cụ thể của bạn, bạn có thể sử dụng một thành phần tùy chỉnh với v-model. Đây là một cú pháp đặc biệt cho phép một cái gì đó gần với ràng buộc hai chiều, nhưng thực sự là một tốc ký cho kiến ​​trúc hướng sự kiện được mô tả ở trên. Bạn có thể đọc về nó ở đây -> https://vuejs.org/v2/guide/components.html#Form-Input-Components-USE-Custom-Events .

Đây là một ví dụ đơn giản:

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>


Các tài liệu nói rằng

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

tương đương với

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

Đó là lý do tại sao chỗ dựa cho đứa trẻ cần được đặt tên giá trị, và tại sao đứa trẻ cần $ phát ra một sự kiện được đặt tên input.


đầu tiên cảm ơn sự phản hồi bạn có thể vui lòng mở rộng hoặc chỉ rõ hơn các tài liệu về sự kiện 'đầu vào' không? nó có vẻ như được xây dựng trong sự kiện
Gal Ziv

Tôi đã thêm một sự làm rõ, và làm cho liên kết đến tài liệu rõ ràng hơn.
asemahle

1
Tôi đã bỏ qua "giá trị" prop cho thành phần và hàm được tạo và nó vẫn hoạt động. bạn có thể giải thích tại sao bạn sử dụng nó?
xetra11

1
Nếu bạn không thêm chỗ dựa, thì sẽ undefinedđến lần thay đổi đầu tiên. Xem fiddle này nơi tôi đã nhận xét props: ['value']. Lưu ý giá trị ban đầu là như thế nào undefined, thay vì hello: jsfiddle.net/asemahle/8Lrkfxj6 . Sau lần thay đổi đầu tiên, Vue tự động thêm một giá trị prop cho thành phần, để nó hoạt động.
asemahle

Tôi đọc ngay quá khứ này trong các tài liệu. Ví dụ làm việc tuyệt vời. +10 nếu tôi có thể!
đất sét

121

Từ tài liệu :

Trong Vue.js, mối quan hệ thành phần cha-con có thể được tóm tắt là các đạo cụ xuống, các sự kiện trở lên. Cha mẹ truyền dữ liệu xuống cho trẻ thông qua các đạo cụ và trẻ gửi tin nhắn cho cha mẹ qua các sự kiện. Hãy xem cách họ làm việc tiếp theo.

nhập mô tả hình ảnh ở đây

Làm thế nào để vượt qua đạo cụ

Sau đây là mã để truyền đạo cụ cho một phần tử con:

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

Cách phát sự kiện

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
    }
  }
})

5
Điều gì xảy ra nếu hàm ọincrement, trong phần tử cha và tôi muốn kích hoạt nó từ thành phần con?
Hamzaouiii

thời điểm khó khăn trong việc nắm bắt khái niệm này, mặc dù đã sử dụng nó nhiều lần bằng cách dán bản sao hacky ...
Yordan Georgiev

1
Tôi sẽ in đồ họa đó và dán nó lên đầu tôi. Cám ơn!
domih

1
Trong ví dụ này, chúng ta không nên có một trình lắng nghe sự kiện được xác định trong thành phần gốc? Một cái gì đó như: `` `Mounted () {this.on ('gia tăng', () => {this.incrementTotal ();}); } `` `
jmk2142

94

Trong thành phần con:

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

Trong thành phần cha mẹ:

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

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

3
Tâm trí của tôi chỉ đơn giản là thổi qua ví dụ này. Bạn không biết tôi đã trải qua bao nhiêu bài hướng dẫn trước khi tôi đến đây ...
suchis life

18

Thành phần con

Sử dụng this.$emit('event_name')để gửi một sự kiện đến thành phần cha.

nhập mô tả hình ảnh ở đây

Thành phần phụ huynh

Để lắng nghe sự kiện đó trong thành phần chính, chúng tôi thực hiện v-on:event_namevà một phương thức ( ex. handleChange) mà chúng tôi muốn thực hiện trên sự kiện đó xảy ra

nhập mô tả hình ảnh ở đây

Làm xong :)


13

Tôi đồng ý với các câu trả lời phát ra sự kiện và mô hình v cho những người ở trên. Tuy nhiên, tôi nghĩ rằng tôi sẽ đăng những gì tôi tìm thấy về các thành phần có nhiều thành phần biểu mẫu muốn phát lại cho cha mẹ của chúng vì đây có vẻ là một trong những bài viết đầu tiên được google trả về.

Tôi biết câu hỏi chỉ định một đầu vào duy nhất, nhưng đây có vẻ là trận đấu gần nhất và có thể giúp mọi người tiết kiệm thời gian với các thành phần vue tương tự. Ngoài ra, không ai đã đề cập đến .syncsửa đổi chưa.

Theo tôi biết, v-modelgiải pháp chỉ phù hợp với một đầu vào trả về cho cha mẹ của họ. Tôi đã mất một chút thời gian để tìm kiếm nó nhưng tài liệu Vue (2.3.0) cho thấy cách đồng bộ nhiều đạo cụ được gửi vào thành phần trở lại cha mẹ (tất nhiên là thông qua phát ra).

Nó được gọi một cách thích hợp là công cụ .syncsửa đổi.

Đây là những gì tài liệu nói:

Trong một số trường hợp, chúng tôi có thể cần ràng buộc hai chiều của Haiti cho một chỗ dựa. Thật không may, ràng buộc hai chiều thực sự có thể tạo ra các vấn đề bảo trì, bởi vì các thành phần con có thể gây đột biến cha mẹ mà không có nguồn gốc của đột biến đó là rõ ràng ở cả cha mẹ và con.

Đó là lý do tại sao thay vào đó, chúng tôi khuyên bạn nên phát ra các sự kiện theo mô hình update:myPropName. Ví dụ: trong một thành phần giả thuyết có chỗ dựa title, chúng ta có thể truyền đạt ý định gán giá trị mới với:

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

Sau đó, phụ huynh có thể nghe sự kiện đó và cập nhật một thuộc tính dữ liệu cục bộ, nếu nó muốn. Ví dụ:

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

Để thuận tiện, chúng tôi cung cấp một tốc ký cho mẫu này với công cụ sửa đổi .sync:

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

Bạn cũng có thể đồng bộ nhiều lần cùng một lúc bằng cách gửi qua một đối tượng. Kiểm tra tài liệu ở đây


Đây là những gì tôi đang tìm kiếm. Cảm ơn rất nhiều.
Thomas

Đây là giải pháp tốt nhất và cập nhật nhất tính đến năm 2020. Cảm ơn rất nhiều!
Marcelo

6

Cách đơn giản hơn là sử dụng this.$emit

Cha.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>

Con.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>

Ví dụ đầy đủ của tôi: https://codesandbox.io/s/update-parent-property-ufj4b


5

Cũng có thể truyền đạo cụ dưới dạng Object hoặc Array. Trong trường hợp này, dữ liệu sẽ được liên kết hai chiều:

(Điều này được ghi chú ở cuối chủ đề: https://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>



0

1) Công ty con: bạn có thể sử dụng như thế này trong thành phần con viết như thế này: this.formValue là gửi một số dữ liệu đến thành phần cha

this.$emit('send',this.formValue)

2) Parrent Compnenet: và trong thẻ thành phần parrent nhận được biến gửi như thế này: và đây là mã để nhận dữ liệu liên kết con đó trong thẻ thành phần cha

@send="newformValue"

0

Một cách khác là chuyển một tham chiếu của setter của bạn từ cha mẹ làm chỗ dựa cho thành phần con, tương tự như cách chúng thực hiện trong React. Giả sử, bạn có một phương thức updateValuetrên cha mẹ để cập nhật giá trị, bạn có thể khởi tạo thành phần con như vậy : <child :updateValue="updateValue"></child>. Sau đó, trên con bạn sẽ có một prop tương ứng: props: {updateValue: Function}và trong mẫu gọi phương thức khi đầu vào thay đổi : <input @input="updateValue($event.target.value)">.


0

Tôi không biết tại sao, nhưng tôi vừa cập nhật thành công dữ liệu gốc bằng cách sử dụng dữ liệu làm đối tượng, :set &computed

Phụ huynh

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

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

Con.vue

<div :set="checkInventory">

props: ['inventory'],

computed: {
            checkInventory() {

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

            },
        }

0

ví dụ của anh ấy sẽ cho bạn biết làm thế nào để chuyển giá trị đầu vào cho cha mẹ trên nút gửi.

Đầu tiên xác định eventBus là Vue mới.

//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
    });
}

0

Tôi nghĩ rằng điều này sẽ thực hiện các mẹo:

@change="$emit(variable)"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.