2019.11.14
Vueでカクテルデータベースをリファインする話#22【双方向バインディングとsync】
僕はいつまでプログラミングの呪縛に囚われなくてはいけないんだろう
正直プログラムの話するのつまんないんだよね()
ただ他に書くこともないので、メモ書きします。今回のテーマは双方向バインディングと呼ばれるものです。
具体的になにかっていうと、言ってしまえば
親コンポーネントと子コンポーネントでデータを同期させたいっていうヤツですね。
これは銘柄の新規入力画面なんですが、何を更新するにしてもフォーム自体はそこまで大きく変わらないんですよね。
メジャーカテゴリ、ミドルカテゴリに関しては上の属カテゴリ部分が変化するぐらいで、あとは銘柄の時のみ度数が加わったり、メジャーカテゴリやミドルカテゴリの登録時は銘柄を最低ひとつは登録しなくてはいけないので、追加で銘柄の入力を強要される、ぐらいの違いしかなくて、使うべきフォームはほぼ同一なんですよね。
つまりこれが何を意味するのかっていうと、
フォーム画面が使い回せるってことなんですよね。
なので、それを見越したコンポーネント作りをしなくてはなりませんネと。
ただ、ここで問題になるのが、普通にやってると
親と子コンポーネントの完全同期が取れません・・・・・・。
親コンポーネントから子コンポーネントにはpropで値を渡すのですが、これはdataでつなげたとしても同期はとれません。
子コンポーネントから、変更を感知したらemitで無理矢理イベントを送信し、それを親コンポーネント側が受け取って値をコピーしてきて.....
あぁ〜〜なんということでしょう!!だるすぎる!!!!!
ということで、もっとスマートに連動させる手段を考えたいと思います。(笑)
それが
.syncと呼ばれるものですね。具体的なリファレンスは
ここを参照に話を進めていこうとおもいます。
重要そうな箇所がここですね。syncっていうのは糖衣構文のようなので、その背景となる動きを理解していないとだめなんですが、それがこのcomputedの部分ですね。
この算術プロパティのsetがwatchの代わりになるわけです。はぁ〜〜〜賢いなこれ。なるほどな。
で、setされたらemitでイベントを発火し、親にぶちこんであげる、と。
ということで、実際に使ってうまくいったコードを載せますね。
<template>
<div>
<div class="flexdiv">
<div style="flex: 1;">
<v-select
:items="select_category_label"
v-model="selectedCategoryValue"
label="カテゴリ"
item-text="label"
item-value="value"
dense
return-object
></v-select>
</div>
<div if="is_need_middle" style="flex: 1;">
<v-select
:items="select_middle_label"
v-model="selectedMiddleValue"
label="ミドルカテゴリ"
item-text="label"
item-value="value"
dense
return-object
></v-select>
</div>
</div>
</div>
</template>
<script>
export default {
props:{
selectedCategory:null,//初期値をここにぶっこむとdataに反映される(はず)
selectedMiddle:null,
//brands:null
},
data() {
return {
first:null,
}
},
computed: {
selectedCategoryValue: {
get() {
return this.selectedCategory;
},
set(value) {
this.$emit('update:selectedCategory', value);
}
},
selectedMiddleValue: {
get() {
return this.selectedMiddle;
},
set(value) {
this.$emit('update:selectedMiddle', value);
}
},
}
}
</script>
要点だけ抜き出すとこんな感じになります。値が代入されたらセッターが呼ばれるっていうのが算術プロパティの仕様ですね。
なので、値が代入されたら、その値とともにupdate:ほげほげイベントをemitします。これを親コンポーネントで受け取るわけですね。
<template>
<div id="container">
<category-secelt v-bind:selected-category.sync="selectedCategory"
v-bind:selected-middle.sync="selectedMiddle"
></category-secelt>
</div>
</template>
<script>
export default {
components: {
CategorySecelt,NameAndDrescription
},
data() {
return {
selectedCategory:null,
selectedMiddle:null
}
},
methods:{
setSelectedCategory(data){
this.selectedCategory=data;
},
setSelectedMiddle(data){
this.selectedMiddle=data;
},
}
}
</script>
親はこんな感じですね。.syncが糖衣構文なので、update:ほげほげが呼ばれたらそれに対応するメソッドが呼ばれます。
これで、子コンポーネントの変更が親に伝わり、親のselectedCategoryとかが連動して更新されるわけですね!!!!
これの実装に1日かかりました。つーーーーらかった!(笑)
これで自由にフォーム部品を組み合わせることができるようになったので、送信部分をつくっていって、管理画面は完成させられるかな・・・・・・・
どうせまたなんか課題が出てくるんだろうけど・・・・・・(笑)