Cách gọi hàm trên thành phần con trên các sự kiện cha


164

Bối cảnh

Trong Vue 2.0, tài liệu và khác chỉ rõ rằng giao tiếp từ cha mẹ đến con xảy ra thông qua các đạo cụ.

Câu hỏi

Làm thế nào để cha mẹ nói với con của nó một sự kiện đã xảy ra thông qua đạo cụ?

Tôi có nên xem một sự kiện được gọi là sự kiện? Điều đó không cảm thấy đúng, cũng không thay thế ( $emit/$on dành cho trẻ em cho cha mẹ và mô hình trung tâm dành cho các yếu tố ở xa).

Thí dụ

Tôi có một bộ chứa cha mẹ và nó cần nói với bộ chứa con của nó rằng có thể thực hiện một số hành động nhất định trên API. Tôi cần để có thể kích hoạt các chức năng.

Câu trả lời:


233

Cung cấp cho thành phần con a refvà sử dụng $refsđể gọi một phương thức trên thành phần con trực tiếp.

html:

<div id="app">
  <child-component ref="childComponent"></child-component>
  <button @click="click">Click</button>  
</div>

javascript:

var ChildComponent = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  }
}

new Vue({
  el: '#app',
  components: {
    'child-component': ChildComponent
  },
  methods: {
    click: function() {
        this.$refs.childComponent.setValue(2.0);
    }
  }
})

Để biết thêm thông tin, xem tài liệu Vue về refs .


13
Bằng cách này, các thành phần cha mẹ và con trở nên kết hợp. Đối với các sự kiện thực tế, giả sử khi bạn không thể thay đổi chỗ dựa để kích hoạt hành động, tôi sẽ đi với giải pháp xe buýt được đề xuất bởi @Roy J
Jared

3
một tài liệu tham khảo cho các tài liệu sẽ là một hữu ích như vuejs.org/v2/guide/iêu
ctf0

Trong thành phần con của tôi, vì những lý do đặc biệt, tôi đã phải sử dụng v-một lần để chấm dứt phản ứng. Do đó, việc chuyển từ chỗ dựa xuống từ cha mẹ sang con cái không phải là một lựa chọn, vì vậy giải pháp này đã có mẹo!
Giăng

1
Câu hỏi của người mới: Tại sao sử dụng refthay vì tạo một chỗ dựa, đồng hồ đó có giá trị của nó sau đó phát ra chức năng khác trong cha mẹ? Tôi có nghĩa là nó có rất nhiều việc phải làm, nhưng sử dụng refthậm chí có an toàn không? Cảm ơn
Irfandy Jip

5
@IrfandyJip - vâng, refan toàn. Nói chung, điều đó không được khuyến khích vì cộng đồng Vue thích truyền trạng thái cho trẻ em và các sự kiện trở lại cho phụ huynh. Nói chung, điều này dẫn đến các thành phần nhất quán, nhất quán hơn (một điều tốt ™). Nhưng, nếu thông tin bạn truyền cho trẻ thực sự là một sự kiện (hoặc lệnh), trạng thái sửa đổi không phải là mẫu phù hợp. Trong trường hợp đó, gọi một phương thức bằng cách sử dụng reflà hoàn toàn tốt, và nó sẽ không gặp sự cố hay gì cả.
joerick

76

Những gì bạn đang mô tả là một sự thay đổi trạng thái trong cha mẹ. Bạn truyền nó cho đứa trẻ thông qua một chỗ dựa. Như bạn đề xuất, bạn sẽ watchchống đỡ. Khi đứa trẻ hành động, nó thông báo cho cha mẹ thông qua một emit, và sau đó cha mẹ có thể thay đổi trạng thái một lần nữa.

Nếu bạn thực sự muốn truyền sự kiện cho một đứa trẻ, bạn có thể làm điều đó bằng cách tạo một chiếc xe buýt (chỉ là một ví dụ Vue) và truyền nó cho đứa trẻ như một chỗ dựa .


2
Tôi nghĩ rằng đây là câu trả lời duy nhất phù hợp với hướng dẫn chính thức về phong cách Vue.JS và các thực tiễn tốt nhất. Nếu bạn sử dụng tốc ký v-modeltrên thành phần, bạn cũng có thể dễ dàng đặt lại giá trị bằng cách phát ra sự kiện tương ứng với ít mã hơn.
Falco

Ví dụ: tôi muốn đưa ra cảnh báo khi người dùng nhấp vào nút. Bạn có đề xuất ví dụ: - xem cờ - đặt cờ này từ 0 đến 1 khi nhấp chuột xảy ra, - làm gì đó - đặt lại cờ
Sinan Erdem

9
Điều đó rất khó chịu, bạn phải tạo thêm propmột đứa trẻ, một tài sản phụ data, sau đó thêm watch... Sẽ thật thoải mái nếu có sự hỗ trợ tích hợp để chuyển sự kiện từ cha mẹ sang con cái. Tình trạng này xảy ra khá thường xuyên.
Илья Зеленько

1
Với tư cách là nhà nước của @ ИЗяяя лльь
Craig

1
Cảm ơn @RoyJ, tôi đoán rằng yêu cầu xe buýt phải tồn tại khi đứa trẻ đăng ký mặc dù vậy, tôi cho rằng toàn bộ ý tưởng gửi sự kiện xuống cho trẻ em không được khuyến khích ở Vue.
Ben Winding

35

Bạn có thể sử dụng $emit$on. Sử dụng mã @RoyJ:

html:

<div id="app">
  <my-component></my-component>
  <button @click="click">Click</button>  
</div>

javascript:

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
    setValue: function(value) {
        this.value = value;
    }
  },
  created: function() {
    this.$parent.$on('update', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
    click: function() {
        this.$emit('update', 7);
    }
  }
})

Ví dụ chạy: https://jsfiddle.net/rjurado/m2spy60r/1/


6
Tôi ngạc nhiên khi nó hoạt động. Tôi nghĩ rằng phát ra cho một đứa trẻ là một mô hình chống lại, hoặc ý định đó là để phát ra chỉ từ trẻ em sang cha mẹ. Có bất kỳ vấn đề tiềm năng với việc đi theo cách khác?
vui vẻ vào

2
Đây có thể không được coi là cách tốt nhất, tôi không biết, nhưng nếu bạn biết bạn đang làm gì thì điều đó không phải là vấn đề. Một cách khác là sử dụng xe buýt trung tâm: vuejs.org/v2/guide/ Kẻ
drinor

14
Điều này tạo ra sự kết hợp giữa trẻ và cha mẹ và được coi là thực hành xấu
morrisl mỏi

5
Điều này chỉ hoạt động vì cha mẹ không phải là một thành phần mà thực sự là một ứng dụng vue. Trong thực tế, điều này là sử dụng ví dụ vue như một chiếc xe buýt.
Julio

2
@Bsienn cuộc gọi đến đây. $ Parent làm cho thành phần này phụ thuộc vào cha mẹ. sử dụng $ emit và đạo cụ để các phụ thuộc duy nhất là thông qua hệ thống liên lạc của Vue. Cách tiếp cận này cho phép cùng một thành phần được sử dụng ở bất cứ đâu trong hệ thống phân cấp thành phần.
morrisl mỏi

6

Nếu bạn có thời gian, hãy sử dụng cửa hàng Vuex để xem các biến (hay còn gọi là trạng thái) hoặc kích hoạt (hay gửi đi) một hành động trực tiếp.


2
do tính phản ứng của vuejs / vuex là aproach tốt nhất, cha mẹ tạo ra một hành động / đột biến làm thay đổi giá trị thuộc tính vuex và ở trẻ em có giá trị được tính toán giống như vuex $ store.state.property.value hoặc "watch" "Phương thức làm gì đó khi vuex" $ store.state.property.value "thay đổi
FabianSilva

6

Không thích cách tiếp cận xe buýt sự kiện bằng cách sử dụng $oncác ràng buộc ở trẻ em trong thời gian create. Tại sao? Các createcuộc gọi tiếp theo (tôi đang sử dụng vue-router) liên kết trình xử lý tin nhắn nhiều lần - dẫn đến nhiều phản hồi cho mỗi tin nhắn.

Giải pháp chính thống của việc truyền đạo cụ từ cha mẹ sang con cái và đặt một người theo dõi tài sản ở trẻ làm việc một chút tốt hơn . Vấn đề duy nhất là đứa trẻ chỉ có thể hành động chuyển đổi giá trị. Truyền cùng một thông điệp nhiều lần cần một số loại sổ sách kế toán để buộc chuyển đổi để trẻ có thể nhận được sự thay đổi.

Tôi đã thấy rằng nếu tôi gói tin nhắn trong một mảng, nó sẽ luôn kích hoạt trình theo dõi con - ngay cả khi giá trị vẫn giữ nguyên.

Cha mẹ:

{
   data: function() {
      msgChild: null,
   },
   methods: {
      mMessageDoIt: function() {
         this.msgChild = ['doIt'];
      }
   }   
   ...
}

Đứa trẻ:

{
   props: ['msgChild'],
   watch: {
      'msgChild': function(arMsg) {
         console.log(arMsg[0]);
      }
   }
}

HTML:

<parent>
   <child v-bind="{ 'msgChild': msgChild }"></child>
</parent>

1
Tôi nghĩ rằng điều này sẽ không hoạt động nếu Trình thông báo luôn có trạng thái giống nhau trên cha mẹ. Ví dụ: Tôi muốn một thành phần mở một phương thức. Phụ huynh không quan tâm nếu trạng thái hiện tại là mở hay đóng, nó chỉ muốn mở phương thức bất cứ lúc nào. Vì vậy, nếu cha mẹ làm điều này.msgChild = true; phương thức được đóng lại, và sau đó cha mẹ làm điều này.msgChild = true, đứa trẻ sẽ không nhận được sự kiện này
Jorge Sainz

1
@JorgeSainz: Đó là lý do tại sao tôi gói giá trị trong một mảng trước khi gán nó vào mục dữ liệu. Không gói giá trị trong một mảng, nó hoạt động giống như bạn chỉ định. Vì vậy, dirChild = true, dirChild = true - không có sự kiện. dirChild = [true], dirChild = [true] - sự kiện!
Jason Stewart

1
Tôi đã không nhìn thấy nó. Cảm ơn đã làm rõ
Jorge Sainz

Điều này là mát mẻ, nhưng cảm thấy một chút hackish. Tôi sẽ sử dụng nó vì nó sạch hơn so với sử dụng hack ref thành phần và giải pháp bus sự kiện ít phức tạp hơn. Tôi biết rằng vue muốn tách rời và chỉ cho phép các thay đổi trạng thái ảnh hưởng đến thành phần nhưng cần có một số cách dựng sẵn để gọi các phương thức của con nếu cần. Có lẽ một công cụ sửa đổi trên một prop mà một khi nó thay đổi trạng thái, bạn có thể tự động đặt lại nó về một giá trị mặc định để người theo dõi sẵn sàng cho sự thay đổi trạng thái tiếp theo. Dù sao cũng cảm ơn bạn đã đăng bài tìm thấy của bạn.
Craig

6

Một cách đơn giản để gọi các phương thức trên các thành phần con là bằng cách phát ra một trình xử lý từ đứa trẻ và sau đó gọi nó từ cha mẹ.

var Child = {
  template: '<div>{{value}}</div>',
  data: function () {
    return {
      value: 0
    };
  },
  methods: {
  	setValue(value) {
    	this.value = value;
    }
  },
  created() {
    this.$emit('handler', this.setValue);
  }
}

new Vue({
  el: '#app',
  components: {
    'my-component': Child
  },
  methods: {
  	setValueHandler(fn) {
    	this.setter = fn
    },
    click() {
    	this.setter(70)
    }
  }
})
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>

<div id="app">
  <my-component @handler="setValueHandler"></my-component>
  <button @click="click">Click</button>  
</div>

Phụ huynh theo dõi các chức năng xử lý con và gọi bất cứ khi nào cần thiết.


Tôi thích giải pháp này sẽ đi đâu nhưng chính xác thì "this.setter" này là gì?
Craig

Đó là tham chiếu hàm setValue được phát ra bởi thành phần con như là một đối số cho sự kiện xử lý.
nilobarp

2

Ví dụ dưới đây là tự giải thích. trong đó refs và sự kiện có thể được sử dụng để gọi hàm từ và đến cha mẹ và con.

// PARENT
<template>
  <parent>
    <child
      @onChange="childCallBack"
      ref="childRef"
      :data="moduleData"
    />
    <button @click="callChild">Call Method in child</button>
  </parent>
</template>

<script>
export default {
  methods: {
    callChild() {
      this.$refs.childRef.childMethod('Hi from parent');
    },
    childCallBack(message) {
      console.log('message from child', message);
    }
  }
};
</script>

// CHILD
<template>
  <child>
    <button @click="callParent">Call Parent</button>
  </child>
</template>

<script>
export default {
  methods: {
    callParent() {
      this.$emit('onChange', 'hi from child');
    },
    childMethod(message) {
      console.log('message from parent', message);
    }
  }
}
</script>

1

Tôi nghĩ rằng chúng ta nên có sự cân nhắc về sự cần thiết của cha mẹ khi sử dụng các phương pháp của trẻ. Thực tế, cha mẹ không cần quan tâm đến phương pháp của trẻ, nhưng có thể coi thành phần con là một thành phần FSA (máy trạng thái hữu hạn). để kiểm soát trạng thái của thành phần con. Vì vậy, giải pháp để xem thay đổi trạng thái hoặc chỉ cần sử dụng chức năng tính toán là đủ


2
Nếu bạn đang nói "cha mẹ không bao giờ nên quan tâm đến việc kiểm soát con cái", có những trường hợp điều này là cần thiết. Hãy xem xét một thành phần đồng hồ đếm ngược. Phụ huynh có thể muốn đặt lại bộ đếm thời gian để bắt đầu lại. Đơn giản chỉ cần sử dụng đạo cụ là không đủ vì đi từ thời gian = 60 đến thời gian = 60 sẽ không sửa đổi chỗ dựa. Bộ hẹn giờ sẽ hiển thị chức năng 'đặt lại' mà cha mẹ có thể gọi là phù hợp.
tbm

0

bạn có thể sử dụng khóa để tải lại thành phần con bằng khóa

<component :is="child1" :filter="filter" :key="componentKey"></component>

Nếu bạn muốn tải lại thành phần với bộ lọc mới, nếu nhấp vào nút, hãy lọc thành phần con

reloadData() {            
   this.filter = ['filter1','filter2']
   this.componentKey += 1;  
},

và sử dụng bộ lọc để kích hoạt chức năng

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.