親イベントで子コンポーネントの関数を呼び出す方法


164

環境

Vue 2.0では、ドキュメントやその他のドキュメントは、親から子への通信が小道具を介して行われることを明確に示しています。

質問

親はどのようにイベントが小道具を介して起こったことを子供に伝えますか?

イベントという小道具を見ればいいですか?それは正しくありませんし、代替案もありません($emit/ $onは親から子へ、ハブモデルは遠方の要素へです)。

親コンテナがあり、APIで特定のアクションを実行しても問題がないことを子コンテナに通知する必要があります。関数をトリガーできるようにする必要があります。

回答:


234

子コンポーネントにa refを指定$refsして、子コンポーネントのメソッドを直接呼び出すために使用します。

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

詳細については、refに関するVueのドキュメントを参照してください。


13
このようにして、親コンポーネントと子コンポーネントが結合されます。実際のイベントの場合、プロップを変更してアクションをトリガーすることができない場合は、@ Roy Jによって提案されたバスソリューションを使用します
Jared

3
ドキュメントへの参照が役立つaswellのだろうvuejs.org/v2/guide/...
ctf0

私の子コンポーネントでは、特別な理由により、v-onceを使用して反応を終了する必要がありました。したがって、親から子に小道具を渡すことは選択肢ではなかったので、この解決策はうまくいきました!
John

1
初心者の質問:値を監視して親の別の関数に出力するプロップref作成する代わりに、なぜ使用するのですか? やるべきことはたくさんありますが、refそれでも安全に使用していますか?ありがとう
Irfandy Jip

5
@IrfandyJip-はい、ref安全です。一般に、Vueコミュニティは状態を子供に渡し、イベントを親に戻すことを好むため、推奨されません。一般的に言えば、これはより孤立した、内部的に一貫したコンポーネントにつながります(良いこと™)。ただし、子に渡す情報が実際にイベント(またはコマンド)である場合、状態の変更は適切なパターンではありません。その場合、を使用してメソッドを呼び出すことrefはまったく問題なく、クラッシュすることも何も起こりません。
joerick

76

あなたが説明しているのは、親の状態の変化です。あなたはそれを小道具を介して子供に渡します。あなたが示唆したように、あなたはwatchその小道具になります。子がアクションを実行すると、を介して親に通知され、親emitは状態を再び変更する場合があります。

本当に子にイベントを渡したい場合は、バス(単なるVueインスタンス)を作成し、それを子としてpropとして渡すことでそれを行うことができます。


2
これが、公式のVue.JSスタイルガイドとベストプラクティスに沿った唯一の答えだと思います。v-modelコンポーネントの短縮形を使用すると、対応するイベントをより少ないコードで発行することで、値を簡単にリセットすることもできます。
Falco

たとえば、ユーザーがボタンをクリックしたときにアラートを送信したいとします。あなたは、たとえば提案でください: -旗を見て-設定し、このフラグが0から1へのクリックが発生した場合、 -リセット旗-何かを
スィナンエルデム

9
これは非常に不快です。prop子にエクストラを作成し、にエクストラプロパティを作成してからdata追加するwatch必要があります。親から子にイベントを転送する組み込みサポートがあった場合は快適です。この状況は頻繁に発生します。
ИльяЗеленько

1
@ИльяЗеленькоによる状態として、それはかなり頻繁に起こります、それは今のところ天の恵みになるでしょう。
クレイグ

1
@RoyJに感謝します。ただし、子がサブスクライブするときにバスプロップが存在する必要があると思いますが、Vueではイベントを子に送信するというアイデア全体が推奨されていないと思います。
ベンワインディング

35

あなたは使用することができる$emit$on。@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);
    }
  }
})

実行例:https : //jsfiddle.net/rjurado/m2spy60r/1/


6
私はそれがうまくいくことに驚いています。子供への放出はアンチパターンであると思った、または放出の意図は子供から親への放出のみであると思った。逆方向に行くことに潜在的な問題はありますか?
2017年

2
これは最善の方法とは見なされないかもしれませんが、わかりませんが、あなたが何をしているのかを知っていれば、問題はありません。もう1つの方法は、中央バスを使用する方法です。vuejs.org
guide

14
これは子供と親の間にカップリングを作成し、悪い習慣と見なされています
morrislaptop '12

5
これが機能するのは、親がコンポーネントではなく、実際にはVueアプリだからです。実際には、これはバスとしてvueインスタンスを使用しています。
フリオロドリゲス

2
@Bsienn this。$ parentの呼び出しにより、このコンポーネントは親に依存します。$ emit toとpropsを使用しているため、依存関係はVueの通信システムを経由するだけです。このアプローチにより、コンポーネント階層のどこでも同じコンポーネントを使用できます。
morrislaptop

6

時間がある場合は、Vuexストアを使用して変数を監視(別名状態)またはアクションを直接トリガー(別名ディスパッチ)してください。


2
最良のアプローチであるvuejs / vuexの反応性により、親ではvuexプロパティ値を変更するアクション/変異を作成し、子ではこれと同じvuex $ store.state.property.valueまたは「ウォッチ"vuex" $ store.state.property.value "が変更されたときに何かを実行するメソッド
FabianSilva

6

の間に子でバインディングを使用するイベントバスアプローチは好きではありませんでした。どうして?後続の呼び出し(私はを使用しています)は、メッセージハンドラーを複数回バインドし、メッセージごとに複数の応答を導きます。$oncreatecreatevue-router

小道具を親から子に渡し、プロパティウォッチャーを子に入れるという正統的な解決策は、少しうまくいきました。唯一の問題は、子が値の遷移にしか対応できないことです。同じメッセージを複数回渡すには、子が変更を認識できるように、トランジションを強制する何らかの簿記が必要です。

メッセージを配列でラップすると、値が同じであっても常に子ウォッチャーがトリガーされることがわかりました。

親:

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

子:

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

HTML:

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

1
msgChildが親で常に同じステータスを持っている場合、これは機能しないと思います。例:モーダルを開くコンポーネントが必要です。親は、現在のステータスが開いているか閉じているかを気にせず、いつでもモーダルを開きたいだけです。したがって、親がthis.msgChild = trueを実行すると、モーダルが閉じられ、親がthis.msgChild = trueを実行すると、子はイベントを受信しません
ホルヘ・サインツ

1
@JorgeSainz:データ項目に割り当てる前に配列の値をラップするのはそのためです。値を配列でラップしないと、指定したとおりに動作します。したがって、msgChild = true、msgChild = true-イベントはありません。msgChild = [true]、msgChild = [true]-イベント!
Jason Stewart、

1
見なかった。説明をありがとう
ホルヘ・サインツ

これはかっこいいですが、少しハックに感じます。コンポーネント参照ハックを使用するよりもクリーンで、イベントバスソリューションほど複雑ではないので、これを使用します。Vueは分離を望んでおり、状態の変更のみがコンポーネントに影響を与えることを許可していますが、必要に応じて子のメソッドを呼び出す組み込みの方法が必要です。おそらく、プロップのモディファイアは、状態が変化すると自動的にデフォルト値にリセットして、ウォッチャーが次の状態変化に備えることができるようにすることができます。とにかくあなたの発見を投稿してくれてありがとう。
クレイグ

6

子コンポーネントのメソッドを呼び出す簡単な分離された方法は、子からハンドラーを発行してから、親からハンドラーを呼び出すことです。

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>

親は子ハンドラー関数を追跡し、必要に応じて呼び出します。


私はこのソリューションがどこに行くのが好きですが、親の「this.setter」は正確には何ですか?
クレイグ

これは、ハンドラーイベントへの引数として子コンポーネントによって発行されるsetValue関数参照です。
nilobarp

2

以下の例は自明です。ここで、参照とイベントを使用して、親子の間で関数を呼び出すことができます。

// 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

親が子のメソッドを使用する必要性について考慮する必要があると思います。実際、親は子のメソッドを考慮する必要はありませんが、子コンポーネントをFSA(有限状態マシン)として扱うことができます。親コンポーネント子コンポーネントの状態を制御するため、ステータスの変化を監視する、または単に計算機能を使用するソリューションで十分です


2
「親が子供を管理することについて自分自身を心配すべきではない」と言っている場合、これが必要な場合があります。カウントダウンタイマーコンポーネントについて考えてみましょう。親はタイマーをリセットして最初からやり直したいと思うかもしれません。time = 60からtime = 60に変更してもプロップが変更されないため、プロップを使用するだけでは不十分です。タイマーは、親が適切に呼び出すことができる「リセット」関数を公開する必要があります。
tbm

0

キーを使用して、キーを使用して子コンポーネントをリロードできます

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

新しいフィルターでコンポーネントをリロードする場合、ボタンをクリックすると子コンポーネントをフィルターします

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

フィルターを使用して関数をトリガーします

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.