有没有办法将组件的实例传递给另一个组件?

时间:2018-04-14 15:22:03

标签: vue.js vuejs2 vue-component

例如,如果我有

<question-dialog />  /* hidden */
<show-dialog-button />
<answer-dialog />  /* hidden */
<show-dialog-button />

我现在希望2个按钮中的每一个都影响它们的前一个对话框 - 第一个按钮改变question-dialog的属性,第二个按钮改变answer-dialog的属性。我认为可以实现此目的的一种方法是将所有4绑定到当前页面的模型,并在mount()中将它们正确连接起来。有没有更好的办法?例如,我可以这样做:

<question-dialog v-id="questionDialog"/>  /* hidden */
<show-dialog-button v-prop:dialog="questionDialog"/>
<answer-dialog />  /* hidden */
<show-dialog-button />

或者可能有一种我不了解的完全不同的方式?

(如果重要的话,我正在使用所有组件的打字稿)

1 个答案:

答案 0 :(得分:1)

您可以将refs传递给这些组件:

<question-dialog ref="questionDialog"/>  /* hidden */
<show-dialog-button v-prop:dialog="$refs.questionDialog"/>

直接调用他们的方法。但有一点需要注意:由于ref不是被动的,只有在mount之后才可用,你必须在mount之后强制更新模板。见下面的演示。

Vue.component('answer-dialog', {
  template: "#answer-dialog",
  data() {
    return {isShown: false}
  },
  methods: {
    show() {
      this.isShown = true;
    }
  }
});
Vue.component('show-dialog-button', {
  template: "#show-dialog-button",
  props: ['dialog'],
  methods: {
    showDialog() {
      this.dialog.show();
    }
  }
})

new Vue({
  el: '#app',
  mounted() {
    // force a re-render after mount, so the $refs are updated in the template
    this.$forceUpdate();
  }
})
<script src="https://unpkg.com/vue"></script>

<template id="answer-dialog">
    <div>answer-dialog - open? {{ isShown }}</div>
</template>

<template id="show-dialog-button">
    <div><button @click="showDialog">Click to toggle dialog</button></div>
</template>

<div id="app">
  <answer-dialog ref="dialogOne"></answer-dialog>
  <show-dialog-button :dialog="$refs.dialogOne"></show-dialog-button>
  
  <answer-dialog ref="dialogTwo"></answer-dialog>
  <show-dialog-button :dialog="$refs.dialogTwo"></show-dialog-button>
</div>

但是,或许更惯用的解决方案是使用事件中心并发出兄弟组件可以侦听的事件:

var eventHub = new Vue(); // use a Vue instance as event hub

Vue.component('answer-dialog', {
  template: "#answer-dialog",
  props: ['dialogId'],
  data() {
    return {
      open: false
    }
  },
  created() {
    eventHub.$on('open-dialog', (e) => {
      if (e === this.dialogId) {
        this.open = true;
      }
    });
  }
});
Vue.component('show-dialog-button', {
  template: "#show-dialog-button",
  props: ['dialogId'],
  methods: {
    showDialog() {
      eventHub.$emit('open-dialog', this.dialogId);
    }
  }
})

new Vue({
  el: '#app'
})
<script src="https://unpkg.com/vue"></script>

<template id="answer-dialog">
    <div>answer-dialog {{ dialogId }} - open? {{ open }}</div>
</template>

<template id="show-dialog-button">
    <div><button @click="showDialog">Click to show dialog {{ dialogId }}</button></div>
</template>

<div id="app">
  <answer-dialog :dialog-id="1"></answer-dialog>
  <show-dialog-button :dialog-id="1"></show-dialog-button>
  
  <answer-dialog :dialog-id="2"></answer-dialog>
  <show-dialog-button :dialog-id="2"></show-dialog-button>
</div>

或者,更好的是,如果可以的话,只需使用v-model

Vue.component('answer-dialog', {
  template: "#answer-dialog",
  props: ['show']
});
Vue.component('show-dialog-button', {
  template: "#show-dialog-button",
  props: ['value']
})

new Vue({
  el: '#app',
  data: {
    dialogOneShow: false,
    dialogTwoShow: false
  }
})
<script src="https://unpkg.com/vue"></script>

<template id="answer-dialog">
    <div>answer-dialog - open? {{ show }}</div>
</template>

<template id="show-dialog-button">
    <div><button @click="$emit('input', !value)">Click to toggle dialog</button></div>
</template>

<div id="app">
  <answer-dialog :show="dialogOneShow"></answer-dialog>
  <show-dialog-button v-model="dialogOneShow"></show-dialog-button>
  
  <answer-dialog :show="dialogTwoShow"></answer-dialog>
  <show-dialog-button v-model="dialogTwoShow"></show-dialog-button>
</div>