基礎からメモ: Vue.js CH5-p166 親から子のイベントやメソッドを使う

$refsを使って、親から子のメソッドを使ったり、イベントを発火させたりできる。

コンポーネント$onを使って自分のイベントをハンドルできるようにしておく。特定のカスタムイベント名で子のメソッドを呼び出すことも可能。

<div id="app">
  <my-component></my-component>
</div>

コンソール出力するだけのopenイベントと、自分のmessageCallメソッドを呼び出すcallイベントを定義。

Vue.component('comp-child', {
  template: '<div>Child Component</div>',
  created: function () {
    // 子コンポーネントのイベント
    this.$on('open', function () {
      console.log('子コンポーネントのイベントを発火!')
    });
    this.$on('call', function () {
      this.messageCall();
    });
  },
  methods: {
    messageCall: function() {
      alert("子コンポーネントのメソッドを起動!");
    }
  }
})

コンポーネントの2つのボタンをクリックすると、それぞれに対応した子のイベントが発火し、子の方で定義された動作が発生する仕組み。

let myComponent = {
  template: `<div class="parent">
  <comp-child ref="child"></comp-child>
  <button v-on:click="handleClick">Child Event Fire!</button>
  <button v-on:click="callChildMeth">Call Child Method!</button>
  </div>`,
  methods: {
    handleClick: function() {
      // 子コンポーネントのイベントを発火
      this.$refs.child.$emit('open');
    },
    callChildMeth: function() {
      // 子コンポーネントのメソッドを呼ぶ
      this.$refs.child.$emit('call');
    }
  }
};

var app = new Vue({
  el: '#app',
  components: {
    'my-component': myComponent
  }
});

ただ子コンポーネントのメソッドを直接起動させたいなら、$emitによるイベント処理を介さずに、親のメソッドを次のように書いても動作する。

callChildMeth: function() {
  // 子コンポーネントのメソッドを直接呼ぶ
  this.$refs.child.messageCall();
}

こちらのほうがシンプル。

子のメソッドの引数を受け取る

コンポーネントの中で使われている子コンポーネントのカスタムタグの属性値にあたる部分は、親のスコープになる。一見、子側のメソッドに渡された引数などを受け取れないように見えるが、その子側のメソッド内で$emitが実行されている場合、その引数を$event変数で参照できる。

$event変数には$emitの第一引数しか格納されていないため、複数の値を渡したい場合は1つのオブジェクトにまとめる。子のカスタムタグで呼び出す親メソッドの引数には$eventの後に、文字列や親のデータなどを第二引数以降として渡すことができる。

<div id="app">
  <my-component></my-component>
</div>
/*
親子関係のコンポーネント
$emit の引数を親のメソッドへ渡す。
*/

Vue.component('comp-child', {
  template: `<div>{{ val }}
    <button v-on:click="callMeth">呼び出し!</button></div>`,
  props: ["val"],
  methods: {
    callMeth: function() {
      this.$emit('childs-event', { id: 1, name: "新しい名前"})
    }
  }
});

let myComponent = {
  template: `<div>
      <comp-child v-on:childs-event="parentsMethod($event, p_message)" class='parent' v-bind:val="p_message"></comp-child>
    </div>`,
  data: function() {
    return { p_message: '親のデータ' }
  },
  methods: {
    parentsMethod: function(c_event, p_data) {
      console.log(c_event.id, c_event.name, p_data);
    }

  },
};

let app = new Vue({
  el: '#app',
  components: {
    'my-component': myComponent
  }
});

コンポーネントのテンプレートにあるボタンを押したときのコンソール表示は

1 "新しい名前" "親のデータ"