The sky is the limit

ハイブリッドアプリ開発、PWAなど効率の良いiOSアプリ、Androidアプリ開発の情報を共有したい。アプリ開発は楽しい。【PWA、AngularJS、Monaca、Cordova、OnsenUI】

【Vue.js】親Componentのデータ値を子Componentで変更する【$emit】

【Vue.js】親Componentのデータ値を子Componentで変更する【$emit】


今回はVue.jsで、親Componentのデータ値を子Componentで変更する方法を記載します。

手順としては

子コンポーネントでカスタムイベントを呼び出す

といった感じです。

■やりたいこと

親コンポーネントが保持するデータを子コンポーネントから更新する

■環境・ライブラリなど

単一ファイルコンポーネントは使っていません。(使う場合でもやり方は同じです)

まずは動きから。
今回の実装は、子コンポーネントでボタンを押すと親コンポーネントの表示が変わるという内容です。

See the Pen vueHowToEmit by duotaro (@duotaro100) on CodePen.

親コンポーネントでカスタムイベントを定義

今回用意するコンポーネントは以下の4つです。

/**************************
    子コンポーネント START
**************************/
Vue.component('money', {
	template:
		'<div>'+
			'<p>welcome money page</p>'+
		'</div>',
	data : function(){
		return {

		}
	}
})

Vue.component('home', {
	template:
		'<div>'+
			'<p>welcome home page</p>'+
		'</div>',
	data : function(){
		return {
			
		}
	}
})

Vue.component('setting', {
	template:
		'<div>'+
			'<p>welcome setting page</p>'+
		'</div>',
	data : function(){
		return {
			
		}
	}
})

Vue.component('menus', {
	template:
		'<div class="child-component">'+
			'<h5>ここは子コンポーネント【Menu】の領域です。</h5>'+
			'<p>Menu</p>'+
			'<!-- クリックすると、changePageを実行します。 -->'+
			'<button v-for="page in pages" @click="changePage(page)">{{page}}</button>'+
		'</div>',
	data : function(){
		return {
			pages : [
				'home',
				'money',
				'setting'
			]
		}
	},
	methods : {
		/**
		 * 親のカスタムイベント「change-current-page」を発火させます。
		 * パラメータのpageを上記イベントで実行されるメソッドにパラメータとして渡します。
		 * 今回の例で言えば「transPage」のパラメータに渡すことになります。
		 * @param {string} page 指定したページ名(コンポーネント名)
		 */
		changePage : function(page){
			console.log(page);
			this.$emit('change-current-page', page)
		}
	}
})
/**************************
	子コンポーネント END
**************************/

上の3つ(money,home,setting)は表示用で、menusは表示切り替え用。
money,home,settingの3つについては表示が切り替わっていることを確認できればいいので、テキストを一文だけ用意しています。
menusは親のデータを更新するためのメソッドなどを用意しています。
詳細は後ほど。今は4つ用意したんだなぁ、と思っていてください。


つぎに、index.htmlです。
ここではVueインスタンスを作成しています。
用意しているデータは現在表示しているページ(コンポーネント)の名前を保持するcurrentPageです。
(※正しくは、currentPageの値に一致したコンポーネントが表示されます。)
メソッドは、currentPageを受け取った値に書き換えるtransPageです。
今回は、このtransPageを子コンポーネントから呼び出して、currentPageを更新するという実装をしています。

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title>親Componentの値を子Componentで変更する</title>
	<script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
	<div id="app">
		<component :is="currentPage"></component>
		<!-- カスタムイベント 呼ばれた時にtransPageを実行します。 -->
		<!-- menuコンポーネントから呼ぶことが可能です。 -->
		<menus @change-current-page="transPage"></menus>
	</div>
</body>
</html>
<script>
var app = new Vue({
  el: '#app',
  data: {
  		/** 現在のページ(コンポーネント名を指定) */
		currentPage : 'home',
  },
  methods : {
  	/**
  	 * currentPageの値を更新します。
  	 * menuコンポーネントでのクリックイベントで呼ばれます。
  	 * @param {string} page menuコンポーネントから送られてくる値
  	 */
  	transPage : function(page){
  		console.log(page);
  		this.currentPage = page;
  	}
  }
})
</script>

component要素については以下を参照

http://www.sky-limit-future.com/entry/vue-pageTransition-active-component

今回の注目点は

<menus @change-current-page="transPage"></menus>

ここでmenuコンポーネントを表示していますが、「change-current-page」というカスタムイベントを定義しています。

@change-current-page="transPage"

このカスタムイベントが呼ばれた時にtransPageメソッドを実行するという内容になっています。
では、このカスタムイベントをどのように呼ぶかですが、先ほどのmenusコンポーネントに戻ります。

Vue.component('menus', {
	template:
		'<div class="child-component">'+
			'<h5>ここは子コンポーネント【Menu】の領域です。</h5>'+
			'<p>Menu</p>'+
			'<!-- クリックすると、changePageを実行します。 -->'+
			'<button v-for="page in pages" @click="changePage(page)">{{page}}</button>'+
		'</div>',
	data : function(){
		return {
			pages : [
				'home',
				'money',
				'setting'
			]
		}
	},
	methods : {
		/**
		 * 親のカスタムイベント「change-current-page」を発火させます。
		 * パラメータのpageを上記イベントで実行されるメソッドにパラメータとして渡します。
		 * 今回の例で言えば「transPage」のパラメータに渡すことになります。
		 * @param {string} page 指定したページ名(コンポーネント名)
		 */
		changePage : function(page){
			console.log(page);
			this.$emit('change-current-page', page)
		}
	}
})

子コンポーネントで、親コンポーネントで定義したカスタムイベントを呼ぶには「$emit」を使います。

$emit('カスタムイベント名')

今回は、menusコンポーネントの中で定義しているchangePageメソッドの中でこれを使用しています。
changePageメソッドはmenusコンポーネント内のボタンを押すことで呼ばれます。

changePage : function(page){
	console.log(page);
	// ここでカスタムイベント「change-current-page」を呼び出します。
	this.$emit('change-current-page', page)
}

先ほど提示した$emitの使い方と少し違います。

this.$emit('カスタムイベント名', '渡すパラメータ')

カスタムイベントに渡すパラメータが必要ない時は先ほどの書き方で大丈夫です。
今回の例で言えば、「$emit('change-current-page', page)」で渡した「page」というパラメータがカスタムイベント「change-current-page」に渡されることになります。
カスタムイベント「change-current-page」はtransPage(page)というメソッドを実行するものでしたので、結果的にtransPage(page)のパラメータとして渡されます。

例えば、

// パラメータが「home」だった場合
this.$emit('change-current-page', 'home')

//「home」がtransPageのパラメータに
transPage('home')

// transPageはcurrentPageの値を変更するものなので
this.currentPage = 'home'

上記のような流れで子コンポーネントからcurrentPageが更新されて、表示されるページが変更されます。


ちなみに、今回の例ではカスタムイベントの呼び出しを子コンポーネントのメソッド内で定義していますが、クリックイベントに直接書くこともできます。

■メソッド内で定義している場合

<!-- ボタンを押すとchangePageが発火 -->
<button v-for="page in pages" @click="changePage(page)">{{page}}</button>
changePage : function(page){
	console.log(page);
	// ここでカスタムイベント「change-current-page」を呼び出します。
	this.$emit('change-current-page', page)
}

■クリックイベントに直接記載する場合

<!-- ボタンを押すとchangePageが発火 -->
<button v-for="page in pages" @click="$emit('change-current-page', page)">{{page}}</button>

注意点としては、メソッド内で$emitを使用する場合は「this.$emit」とする必要があります。
一方で、クリックイベントに書く時には必要ありません。
以上が、親Componentのデータ値を子Componentで変更する方法でした。