Photo by Christopher Robin Ebbinghaus on Unsplash
こんにちは!
個人開発者の南です。
VueJsでコンポーネントごとにわけている時に、コンポーネントにあるデータを他のコンポーネントに分けるにはどうすればいいのだろう?っと思ったことがありました。
今回は忘れた時に思い出せる用のメモとして、コンポーネント間のデータのやりとりの方法について紹介します!
コンポーネント間でデータをやりとりする方法
コンポーネント間でデータをやりとりするために必要なことは、次の2つです。
・v-bind:valueで渡したい値を指定。
・$emitで更新した値を親のコンポーネントに渡す。
の2点を基本的には、行っていきます。
実際のコードを書くと次のような感じになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#親コンポーネント <template> <div class="container" id="app"> <div v-if="page == 0"> <div class="columns"> <div class="column"> <div class="message"> <div class="message-header"> <h2>{{ post_data.post_title }}</h2> </div> <div class="message-body"> <h2>{{ post_data.desc }}</h2> <button type="button" @click="change_form">値を更新する</button> </div> </div> </div> </div> </div> <div v-if="page == 1"> <another-component @send_data_from_another_form="set_data" :desc="post_data.desc" :post_title="post_data.post_title"></another-component> </div> </div> </template> <script> import another from './another.vue'; export default { components:{ 'another-component' : another, }, data () { return { post_data : { post_title: '最初のタイトル', desc: '最初の内容です。', }, page: 0 } }, methods:{ change_form(){ this.page = 1; }, set_data( data_from_another_component ){ let data = data_from_another_component; this.post_data.post_title = data.new_post_title this.post_data.desc = data.new_desc this.page = data.new_page_num } } } </script> <style> #app{ margin-top:60px; width: 400px; } </style> |
そして、別のコンポーネントは下記のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <div id="app" class="container"> <div class="field"> <label class="label">タイトル</label> <div class="control"> <input type="text" v-model="new_post_title"> </div> <div class="field"> <label class="label">詳細</label> <div class="control"> <input type="text" v-model="new_desc"> </div> <div class="control"> <button type="button" @click="submit">更新</button> </div> </div> </div> </div> </template> <script> export default { props: ['post_title', 'desc'], data () { return { new_post_title: '', new_desc: '', new_page_num: 0 } }, mounted() { this.new_post_title = this.post_title; this.new_desc = this.desc; }, methods:{ submit(){ this.$emit( 'send_data_from_another_form', this ); } } } </script> |
作成したコンポーネントの実際の動き
実際に表示してみると、親コンポーネントは下記のように表示されます。
そして、親コンポーネントの「値を更新する」という部分をクリックすると、子コンポーネント(another-component)に移動します。
そして、子コンポーネントのフォームの値を変更します。
値の更新が終わったら、「更新」をクリックします。
すると、親コンポーネントの表示に切り替わります。
表示が切り替わった後は、子コンポーネントで変更した内容が反映されています。
これが上記のコンポーネントの基本的な流れです。
コードの解説
動きがわかったところで、コードの解説をしていきます。
ここでは、次の順番にコードを解説していきます。
1.親コンポーネントのコードについて
2.子コンポーネントのコードについて
親コンポーネントのコードについて
最初のところで紹介したように親コンポーネントは次のようなコードでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#親コンポーネント <template> <div class="container" id="app"> <div v-if="page == 0"> <div class="columns"> <div class="column"> <div class="message"> <div class="message-header"> <h2>{{ post_data.post_title }}</h2> </div> <div class="message-body"> <h2>{{ post_data.desc }}</h2> <button type="button" @click="change_form">値を更新する</button> </div> </div> </div> </div> </div> <div v-if="page == 1"> <another-component @send_data_from_another_form="set_data" :desc="post_data.desc" :post_title="post_data.post_title"></another-component> </div> </div> </template> <script> import another from './another.vue'; export default { components:{ 'another-component' : another, }, data () { return { post_data : { post_title: '最初のタイトル', desc: '最初の内容です。', }, page: 0 } }, methods:{ change_form(){ this.page = 1; }, set_data( data_from_another_component ){ let data = data_from_another_component; this.post_data.post_title = data.new_post_title this.post_data.desc = data.new_desc this.page = data.new_page_num } } } </script> <style> #app{ margin-top:60px; width: 400px; } </style> |
この親コンポーネントのコードでは、次のような処理を書いています。。
・v-ifを使ってpageの数字によって表示するものを切り替える。
・「値を更新する」というボタンが押されたら画面を切り替える。
・子コンポーネントにデータをバインドして、現在のデータを渡す。
・子コンポーネントから、更新されたという通知を受け取る。
・通知を受け取ったら、データの更新する。
それぞれ順に紹介していきます。
v-ifを使ってpageの数字によって表示するものを切り替える。
まずは、v-ifで表示するページを切り分けています。
コード中の「data()」の箇所で定義されている「page」が0の時は、親コンポーネントを表示。
1の時は、子コンポーネントを表示という風にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#親コンポーネントを表示 <div v-if="page == 0"> <div class="columns"> <div class="column"> <div class="message"> <div class="message-header"> <h2>{{ post_data.post_title }}</h2> </div> <div class="message-body"> <h2>{{ post_data.desc }}</h2> <button type="button" @click="change_form">値を更新する</button> </div> </div> </div> </div> </div> #子コンポーネントを表示 <div v-if="page == 1"> <another-component @send_data_from_another_form="set_data" :desc="post_data.desc" :post_title="post_data.post_title"></another-component> </div> |
「値を更新する」というボタンが押されたら画面を切り替える。
「値を更新する」というボタンがクリックされると、「methods」の中で定義されいてる「change_form()」という関数が走ります。
この関数はクリックされると、「page」の値を0から1に変更するという処理になっています。
これにより、子コンポーネントの表示に切り替わります。
1 2 3 4 5 6 7 8 9 |
#クリックすると、「change_form」関数が走る。 <button type="button" @click="change_form">値を更新する</button> methods:{ #クリックされるとpageを1に更新して、表示を切り替え。 change_form(){ this.page = 1; }, } |
子コンポーネントにデータをバインドして、現在のデータを渡す。
親コンポーネントに記述されている「
実は、これで子コンポーネントに親コンポーネントのデータを渡しています。
「:desc=”post_data.desc”」は、子コンポーネントでは、「this.desc」で参照することができます。
「:post_title=”post_data.post_title”」は、子コンポーネントでは、「this.post_title」というように参照できます。
つまり、「:」以降に参照したい時の名前を定義して、「=」以降にどんなデータを渡したいかを定義します。
これによって、子コンポーネントでは「:」以降で定義した名前でデータを使うことができます。
あとで解説しますが、子コンポーネントでは、次のようにデータを参照します。
1 2 3 4 5 6 7 8 9 10 11 |
export default { #親コンポーネントで渡された値を定義する。 props: ['post_title', 'desc'], #初期値に設定する。 mounted() { this.new_post_title = this.post_title; this.new_desc = this.desc; }, } |
子コンポーネントから、更新されたという通知を受け取る。
親コンポーネントに記述されている「
これは、子コンポーネントで記述されている下記のコードに対応しています。
1 2 3 4 5 |
submit(){ this.new_post_title = this.new_post_title; this.new_desc = this.new_desc; this.$emit( 'send_data_from_another_form', this ); } |
あとで解説しますが、子コンポーネントの更新ボタンをクリックすると、「submit」関数が走るようになっています。
この中で、「this.$emit( ‘send_data_from_another_form’, this )」の中にある、「send_data_from_another_form」と親コンポーネントの「@send_data_from_another_form」が対応しています。
これは、JavaScriptのイベントリスナーようなもので、子コンポーネントで「this.$emit( ‘send_data_from_another_form’, this )」が走ると、その後に親コンポーネントの「@send_data_from_another_form」で定義されている「set_data」関数が走ります。
つまり、子コンポーネントで「$emit」して、「@」でその関数が走ったという通知を受け取ることができます。
通知を受け取ったら、データの更新する。
子コンポーネントで「this.$emit( ‘send_data_from_another_form’, this )」が走ると、親コンポーネントで定義されている「set_data」関数が走ります。
あとは、最初引数の中で子コンポーネントで更新されたデータが渡ってくるので、それをローカルのデータにセットすることで値を更新することができます。
1 2 3 4 5 6 7 8 9 10 |
#子コンポーネントの$emitが走ると処理される。 set_data( data_from_another_component ){ #子コンポーネントのデータをローカルのデータにセットして値を更新。 let data = data_from_another_component; this.post_data.post_title = data.new_post_title this.post_data.desc = data.new_desc this.page = data.new_page_num } |
親コンポーネントのコードの解説は以上になります。
子コンポーネントのコードについて
次は子コンポーネントのコードについてです。
子コンポーネントのコードは下記のようなコードでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<template> <div id="app" class="container"> <div class="field"> <label class="label">タイトル</label> <div class="control"> <input type="text" v-model="new_post_title"> </div> <div class="field"> <label class="label">詳細</label> <div class="control"> <input type="text" v-model="new_desc"> </div> <div class="control"> <button type="button" @click="submit">更新</button> </div> </div> </div> </div> </template> <script> export default { props: ['post_title', 'desc'], data () { return { new_post_title: '', new_desc: '', new_page_num: 0 } }, mounted() { this.new_post_title = this.post_title; this.new_desc = this.desc; }, methods:{ submit(){ this.$emit( 'send_data_from_another_form', this ); } } } </script> |
このコードでは、次のようなことをしています。
1.propsでどんなデータが渡ってくるかを定義。
2.mountedで、親コンポーネントのデータをセット。
3.更新ボタンがクリックされたら、submit関数を走らせる。
4.submit内で、v-modelで定義されている、inputの内容を反映する。
5.$emitで親コンポーネントにデータを渡す。
propsでどんなデータが渡ってくるかを定義。
親コンポーネントでは、次のように「:desc」のように子コンポーネントに渡すデータを定義していました。
1 |
<another-component @send_data_from_another_form="set_data" :desc="post_data.desc" :post_title="post_data.post_title"></another-component> |
子コンポーネントでは必ず「props」を使って、どんなデータが渡ってくるかを定義しておく必要があります。
1 |
props: ['post_title', 'desc'], |
「props」に定義することで、親コンポーネントから渡されたデータを使用することができます。
mountedで、親コンポーネントのデータをセット。
「mounted」関数は画面が表示された時に、走る関数です。
ここでは、次のように親コンポーネントのデータを反映するという処理をしています。
1 2 3 4 5 |
mounted() { #親コンポーネントから渡されたデータを子コンポーネントの変数に反映。 this.new_post_title = this.post_title; this.new_desc = this.desc; }, |
更新ボタンがクリックされたら、submit関数を走らせる。
「更新」ボタンをクリックすることで、「@click=”submit”」で定義されているsubmit関数が走ります。
#submit関数を走らせる。
$emitで親コンポーネントにデータを渡す。
submit関数が走ったら、あとは関数内の「$emit」を使って子コンポーネントのデータを親コンポーネントに送ります。
1 2 3 4 |
submit(){ #親コンポーネントに通知と、データを送る。 this.$emit( 'send_data_from_another_form', this ); } |
$emitはVueJsで定義されているイベントの内の1つで、主にイベントのトリガーを送ることができます。
第一引数にイベント名、第二引数に送りたいデータなどをセットします。
今回のコードでも、「send_data_from_another_form」というのをトリガーにして、親コンポーネントにデータを送っています。
まとめ
今回は、親コンポーネントのデータを子コンポーネントに渡す方法について紹介しました。
もう一度紹介すると、親コンポーネント内で渡したいデータを「:data=”渡したいデータ”」のように定義。
子コンポーネント内で「props[‘data’]」のように定義してから、親コンポーネントの値を使うという流れでした。
また、子コンポーネントのデータを親コンポーネントに渡す場合は「$emit」を使って、親コンポーネントにイベントを送って、処理をするという流れになります。
今回の記事が僕と同じように、親コンポーネントと子コンポーネントでデータのやり取りをする方法を探している方のお役に立てたら嬉しいです。