日記
正しいものを学ぼうとしているのだけれども、絶対に正しいというものはなくって、今の状況(要件だったり、納期だったり、環境だったり、自分の実力だったり)を考えみて、自分はこうすべきだと思ったと言えるようになればいいのかなと最近思った。
前回からの続き
www.tohuandkonsome.site
だいぶ時間が空いてしまいましたが、前回からの続きです。
前回は、こんなコードを書きました。
前回のindex.html
<h1>スタイルをバインドする</h1>
<div class="box1" @click="sayHello" v-bind:style="styleObject">box1</div>
<div class="box2" @click="sayHello" v-bind:style="styleObject">box2</div>
前回のapp.js
let box1 = new Vue({
el:'.box1',
data: {
pointX:0
styleObject:{
transform: 'translateX('+this.pointX+'px)'
},
methods:{
moveRight:function(event){
this.pointX++;
this.styleObject.transform = convertTranslate(this.pointX);
},
}
})
function convertTranslate(value)
{
return 'translate(' + value + 'px)'
}
これをブラウザでみると、以下ように箱をクリックすると、それが右に動くというどうしようもないものが見れます。
今回やりたいこと
box1をクリックするとbox1とbox2も動くということをやってみたいと思います。
修正案:右に動くという値を共有する
まっさきに思いついたのがこちらです。
クリックするたびにカウントアップされるpointXをbox1とbox2で共有してあげます。
共有する値はオブジェクト型じゃないと、box1とbox2でそれぞれ値を持ってしまうので注意が必要ですね。
修正したapp.js
let globalData = {pointX:sharePointX};
let box1 = new Vue({
el:'.box1',
data: {
pointX:globalData,
styleObject:{
transform: 'translateX(0px)'
},
},
methods:{
moveRight:function(event){
this.pointX.pointX++
console.log(this.pointX.pointX);
this.styleObject.transform = convertTranslate(this.pointX.pointX);
},
},
})
let box2 = new Vue({
el:'.box2',
data: {
pointX:globalData,
styleObject:{
transform: 'translateX(0px)'
},
},
methods:{
moveRight:function(event){
this.styleObject.transform = convertTranslate(this.pointX.pointX);
},
}
})
さて、こちらを実行すると、、、
うまくいきません。
box1をクリックしてもbox2が右に動くことはありません。
値が共有できていないのでは?というとそういうわけでもなさです。
というのも、box2をクリックすると、思い出したかのように今までbox1でカウントアップした分の値でbox2が右に動きます。
どうやら、box2はbox1によってpointXが変更されたとしても、その変化に気づくようにはなってないのだと思います。
こういうことをしたい場合、どうしたらよいのでしょうか。
修正案:公式のアドバイスに従う
ちゃんと公式に書いてありました。
jp.vuejs.org
ときどき、互いに親子関係ではない2つのコンポーネントが互いに通信する必要があるかもしれません。簡単なシナリオとして、空の Vue インスタンスを中心のイベントバスとして使用することができます:
どうも空っぽのVueインスタンスを作って、コンポーネントのときに学んだコンポーネント間のデータやらイベントのやりとりの方法を使うことでうまくできるみたいです。
今一度のapp.js
var bus = new Vue();
let box1 = new Vue({
el:'.box1',
data: {
pointX:globalData,
styleObject:{
transform: 'translateX(0px)'
},
},
methods:{
moveRight:function(event){
this.pointX.pointX++
console.log(this.pointX.pointX);
this.styleObject.transform = convertTranslate(this.pointX.pointX);
bus.$emit('ugoiteyo')
},
},
})
let box2 = new Vue({
el:'.box2',
data: {
pointX:globalData,
styleObject:{
transform: 'translateX(0px)'
},
},
methods:{
moveRight:function(event){
this.styleObject.transform = convertTranslate(this.pointX.pointX);
},
},
created:function(){
bus.$on('ugoiteyo', this.moveRight);
}
})
- box1をクリックすると、イベント「ugoiteyo」が発火する
- box2のイベントリスナー「ugoiteyo」が動く
こんな感じで、今度はやりたいことを実現できました!
とはいえ
こんな面倒なことはせずに、HTMLの構成を以下のように、box1とbox2を包んであげる要素「boxes」を置いて、
index.html
<div class="boxes">
<div class="box1" @click="moveRight" v-bind:style="styleObject">box1</div>
<div class="box2" @click="moveRight" v-bind:style="styleObject">box2</div>
Vueインスタンスのセレクタも「boxes」をしてあげれば、やりたいことはできちゃいますね。
app.js
let boxes = new Vue({
el:'.boxes',
data: {
pointX:0,
styleObject:{
transform: 'translateX(0px)'
},
},
methods:{
moveRight:function(event){
this.pointX++
console.log(this.pointX);
this.styleObject.transform = convertTranslate(this.pointX);
},
},
})