Vue.jsの基礎を学ぶ(3日目)

#監視プロパティ(ウォッチャ)

特定のデータ、または算出プロパティの状態を監視して変化があった時に登録した処理を自動的に実行できるもの。

ex) 検索フォームの値が変わったタイミングで自動的にAjax通信を使って結果を一覧表示。

##簡単な監視プロパティのプログラム

```html
<div id="app">
  <p>
    {{ message }}
  </p>
  <p>
    <input type="text" v-model:value="message">
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
  	message: 'Hello Vue.js!'
  },
  watch: {
    message: function(newValue, oldValue) {
      console.log('new: %s, old: %s', newValue, oldValue)
    }
  }
})
```

watch:以下にウォッチャーを定義して処理を書いていく。
ここでは、messageプロパティの値に変化があった時にコンソールに古い値と新しい値を出力している。

newValueとoldValueの名前はなんでも大丈夫。


##km m mmを相互変換するアプリ

```html
<div id="app">
  <p>
    <input type="text" v-model:value="km">km
  </p>
  <p>
    <input type="text" v-model:value="m">m
  </p>
  <p>
    <input type="text" v-model:value="mm">mm
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
  	km: 0,
    m: 0,
    mm: 0
  },
  watch: {
    //ウォッチャは第一引数に新しい値が入る？
    km: function(value) {
      this.km = value
      this.m = value * 1000
      this.mm = value * 1000000
    },
    m: function(value) {
      this.km = value / 1000
      this.m = value
      this.mm = value * 1000
    },
    mm: function(value) {
      this.km = value / 1000000
      this.m = value / 1000
      this.mm = value
      
    }
  }
})
```

ウォッチャーはfunction(第一引数,第二引数)と書く時に、

第一引数に変更後の値、第二引数に変更前の値が入るということでいいんだよね。


##算出プロパティで書いた方がいい場合。

fitstNameとlastNameプロパティを使ってフルネームを出力するプログラム

```html
<div id="app">
  <p>
    firstName: <input type="text" v-model:value="firstName">
  </p>
  <p>
    lastName: <input type="text" v-model:value="lastName">
  </p>
  <p>
    fullName: {{ fullName }}
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
/* var app = new Vue({
  //options
  el: '#app',
  data: {
    firstName: '',
    lastName: '',
    fullName: ''
  },
  watch: {
    firstName: function(value) {
      this.fullName = value + ' ' + this.lastName
    },
    lastName: function(value) {
      this.fullName = this.firstName + ' ' + value
    }
  }
}) */
var app = new Vue({
  //options
  el: '#app',
  data: {
    firstName: '',
    lastName: ''
  },
  computed: {
    fullName: function() {
    	return this.firstName + ' ' + this.lastName
    }
  }
})
```

監視プロパティの場合は、firstNameが変わった時とlastNameが変わった時のどちらも処理を書かなければいけないが、

算出プロパティの場合は一つで済む。

#監視プロパティのオプション

##deep

ネストされたオブジェクトも監視するオプション

```html
<div id="app">
  <ul>
    <li v-for="color in colors">
      {{ color.name }}
    </li>
  </ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    colors: [
     { name: 'Red'},
     { name: 'Green'},
     { name: 'Blue'}
    ]
  },
  watch: {
    colors: {
    	handler: function(newValue, oldValue) {
        conlole.log('Update!')
      },
      deep: true
    }
  }
})
```

ネストされたものまで監視するので、RedやGreenの値が変わるとコンソールに出力される

###deep補足

deepはネストされたものを監視するオプション。そのため、ネストされた物の古い値、新しい値を取得することはできるが、配列やオブジェクトそのものの変化があった場合の古い値と新しい値の取得は難しい。


##immediate

初期読み込み時に監視プロパティを呼び出したい時に使う。


immediate:trueとすると、初期読み込み時にもコンソールに出力したりということができる。


#クラスとスタイルのバインディング

##v-bind:classに動的にクラスを切り替えたい

```html
<div id="app">
  <p>
    Hello <span v-bind:class="{large: isLarge}">Vue.js!</span>
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    isLarge: true 
  }
})
```

v-bind:class="~~"には今まではプロパティ名が入った。

今回は

```html
<span v-bind:class="{large: isLarge}">Vue.js!</span>
```
とすることで、isLargeがtrueの時だけクラス名にlargeが付与されるという状態にすることができる。


#複数のクラスを動的に切り替える

```html
<div id="app">
  <p>
    Hello 
    <span v-bind:class="{large: isLarge, 'text-danger': hasError}">
      Vue.js!
    </span>
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}

.text-danger {
  color: red;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    isLarge: true,
    hasError: true
  }
})
```

複数のクラスを動的に切り替えたいときは、

```html
<span v-bind:class="{large: isLarge, 'text-danger': hasError}"></span>
```
このように、,で区切ってシングルクォートのなかにクラス名を入れ、:プロパティ名とする。

注意点としては、ハイフンを含むクラス名の場合、シングルクォートにしなければいけないらしい。

これで、isLargeとhasErrorの両方がtrueの場合、文字が大きく、赤色になる。

#プレーンなクラス属性と共存

v-bind:classはプレーンなクラス属性と共存できる

```html
<div id="app">
  <p>
    Hello 
    <span class="bg-gray text-blue" v-bind:class="{large: isLarge, 'text-danger': hasError}">
      Vue.js!
    </span>
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}

.text-danger {
  color: red;
}

.bg-gray {
  background-color: gray;
}

.text-blue {
  color: blue;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    isLarge: true,
    hasError: true
  }
})
```


```html
<span class="bg-gray text-blue" v-bind:class="{large: isLarge, 'text-danger': hasError}">
      Vue.js!
    </span>
```
bg-grayとtext-blueというプレーンなクラスがv-bind:classと共存している。

この場合、どちらのクラス属性も適応される。text-blueとtext-dangerはどちらも文字の色を指定するクラスなので、cssで後ろに書いてある方が優先される。


#配列構文によるクラスのデータバインディング

v-bind:classにクラスのリストを適用する配列を渡したい。

```html
<div id="app">
  Hello <span v-bind:class="[largeClass, dangerClass]">Vue.js</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}

.text-danger {
  color: red;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    largeClass: 'large',
    dangerClass: 'text-danger',
  }
})
```

注目すべきは

```html
<span v-bind:class="[largeClass, dangerClass]">Vue.js</span>
```

v-bind:class=""内に配列を入れている点だ。

これで、largeClassとdangerClassの複数のクラスをセットすることができた。


#オブジェクトデータの利用

先ほどクラス名を配列として渡した。
それだと、viewの可読性が落ちてしまう可能性があるので、オブジェクトとして渡してみよう。

```html
<div id="app">
  Hello <span v-bind:class="classObject">Vue.js</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}

.text-danger {
  color: red;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    classObject: {
    	large: true,
      //クラス名にハイフンが入っているため
      'text-danger': true
    }
  }
})
```

この場合、classObjectを用意して、その中にlargeとtext-dangerというクラス名を入れている。

それを

```html
<span v-bind:class="classObject">Vue.js</span>
```

と書くだけで、複数のクラスを適用することができる。

#クラスの条件に三項演算子を使う

```
condition? expr1 : expr2
```

condition?がtrueならexpr1。falseならexpr2が実行される。


```html
<div id="app">
  Hello <span v-bind:class="[isLarge ? largeClass : '', dangerClass]">Vue.js</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```css
.large {
  font-size: 36px;
}

.text-danger {
  color: red;
}

.bg-gray {
  background-color: gray;
}

.text-blue {
  color: blue;
}
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    largeClass: {
    	large: true,
      'bg-gray': true
        
    },
    dangerClass: {
      'text-danger': true,
    },
    isLarge: true
    
  }
})
```

注目すべきは

```html
<span v-bind:class="[isLarge ? largeClass : '', dangerClass]">Vue.js</span>
```

まず、

```
isLarge ? largeClass : ''
```

この部分が三項演算子にあたる。
trueの時にlargeClass適用

また、三項演算子に関係なく、適用したいクラスがあるときは、

[]で囲んで、,クラス名を追加することで常時適用したいクラス名を入れられる。

これは、上段のクラス名を配列で指定したいの所で構文を確認しよう。

#インラインスタイルのデータバインディング

v-bind:styleを使うとインラインスタイルを設定できる。

```html
<div id="app">
  Hello <span v-bind:style="{ color: color, fontSize: fontSize + 'px'}">Vue.js</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
   color: 'blue',
   fontSize: 30
  }
})
```

注目すべきは

```html
<span v-bind:style="{ color: color, fontSize: fontSize + 'px'}">Vue.js</span>
```

注意点としては、cssのプロパティ名をキャメルケースで書かなければいけないこと。
スネークケースで書く場合は、

```
'font-size':
```
このようにシングルクォートで囲む。


#インラインスタイルのデータバインディングにオブジェクトのデータを渡す

```html
<div id="app">
  Hello <span v-bind:style="styleObject">Vue.js</span>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    styleObject: {
      color: 'blue',
      fontSize: '36px'
    }
  }
})
```

先ほどは

```html
<span v-bind:style="{ color: color, fontSize: fontSize + 'px'}">Vue.js</span>
```

このようにデータを渡していたが、

今回は、それをオブジェクトにして渡している。

```html
<span v-bind:style="styleObject">Vue.js</span>
```

こちらの方が可読性が上がる。


#条件付きレンダリング

条件によって動的にコンテンツの表示を切り替える

##v-if v-else

真偽値がtrueの時YES,falseの時NOと表示したい

```html
<div id="app">
  <p v-if="toggle">
    YES
  </p>
  <p v-else>
    NO
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```
```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    toggle: true
  }
})
```

注意点としては、v-elseはv-ifの直後にないといけない。


##v-else-if

信号の色によってGoやStopなどを表示するプログラムを作りたい


```html
<div id="app">
  <p v-if="color === 'red'">
    Stop
  </p>
  <p v-else-if="color === 'yellow'">
    Caution
  </p>
  <p v-else-if="color === 'blue'">
    Go
  </p>
  <p v-else>
    Not red/yellow/blue
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    color: 'red'
  }
})
```

rubyのif elsifと同じだね。


##v-show

真偽値がtrueの時Hello Vue.js!と表示し、falseの時は表示しないプログラムを作りたい。

```html
<div id="app">
  <p v-show="toggle">
    Hello Vue.js!
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    toggle: true
  }
})
```

ちなみに、v-showディレクティブはv-elseで場合分けすることができないのでご注意を。


#イベントハンドリング

##インラインメソッドハンドラ

クリック数のカウンタを実装したい

```html
<div id="app">
  <p>
    {{ counter }}
  </p>
  <button v-on:click="counter++">
    Click!
  </button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    counter: 0
  }
})
```

今まではv-on:click="メソッド名"だったのに対し、

今回は

```html
<button v-on:click="counter++">
    Click!
  </button>
```

このように、" "の中に直接処理を書いているのに注目。

これでボタンをクリックすると、１ずつカウンタが増えていく。


##メソッドイベントハンドラ

上記だとタグの中に処理を書かなければいけないので限界がある。
なので一般的には、v-on:click="メソッド名"とし、jsにメソッドの処理を書いていくのが普通。

```html
<div id="app">
  <p>
    {{ counter }}
  </p>
  <button v-on:click="clickHandler">
    Click!
  </button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    counter: 0
  },
  methods: {
    clickHandler: function() {
    	this.counter++
    }
  }
})
```
注意点としては、定義したメソッドに引数が不要な場合は、view側でも()を付けずに呼び出すことができる。

#イベントオブジェクトの参照

イベントハンドラのメソッドに引数を指定すると、イベントオブジェクトを取得することができる。

```
clickHandler: function(event) {...}
```

ボタンをクリックするとVue.js!と表示されるプログラムを作りたい

```html
<div id="app">
  <p>
    {{ message }}
  </p>
  <button v-on:click="clickHandler('Vue.js!')">
    Click!
  </button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clickHandler: function(message) {
      this.message = message      
    }
  }
})
```

view側のメソッドの呼び出しで、clickHandler('Vue.js!)と引数を指定している。

そして、js側でfunction(message)とすることでview側で指定した引数を受け取っている。

##$eventの使い方

上記のように、メソッドに引数を指定している、かつイベントオブジェクトを取得したいときはどうすればいいのか？

```html
<div id="app">
  <p>
    {{ message }}
  </p>
  <button v-on:click="clickHandler($event, 'Vue.js!')">
    Click!
  </button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clickHandler: function($event, message) {
      this.message = message
      console.log($event)
    }
  }
})
```

このように、view側と、js側の第一引数に$eventを指定してあげれば良い。

#イベント修飾子

Vueではv-onのためにイベント修飾子を提供している。

##.once

イベントハンドラを1回だけ実行する

```html
<div id="app">
  <p>
    {{ message }}
  </p>
  <button v-on:click.once="clickHandler">
    Click!
  </button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clickHandler: function() {
      this.message = new Date().toLocaleTimeString()
    }
  }
})
```

注目すべきは

```html
<button v-on:click.once="clickHandler">
    Click!
  </button>
```

このように v-on:clickの後ろに.onceをつけることで、

ボタンが押された一度だけ現在時刻を表示することができる。


##キー修飾子

特定のキーボードのキーが押された時にのみ、特定のイベントハンドラを呼び出すことができる。

構文

```
v-on:keyup.キーの種類
```

escキーを押したらテキスト入力の値をクリアにするプログラムを作りたい。

```html
<div id="app">
  <!-- キー番号27はescキーである -->
  <input type="text" v-on:keyup.27="clear" v-model="message">
  <!-- .escとエイリアスを指定することができる -->
  <input type="text" v-on:keyup.esc="clear" v-model="message">
  <!-- escまたは、spaceキー、またはupキーを押すとクリアされる -->
  <input type="text" v-on:keyup.esc.space.up="clear" v-model="message">
  <p>
    {{ message }}
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clear: function() {
      this.message = ''
    }
  }
})
```

注目すべきは

```html
<input type="text" v-on:keyup.27="clear" v-model="message">
```

タグにv-onとv-model両方指定できることも注意しておこう。

##システム修飾子

ctrl + cキーが押されたらテキスト入力欄をクリアする

```html
<div id="app">
  <input type="text" v-on:keyup.ctrl.67="clear" v-model="message">
  <p>
    {{ message }}
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clear: function() {
      this.message = ''
    }
  }
})
```

shiftキーを押しながらクリックするとアラートが呼ばれるプログラムを作りたい

```html
<div id="app">
  <input type="text" v-on:keyup.ctrl.67="clear" v-model="message">
  <div v-on:click.shift="clickHandler">Click me!</div>
  <p>
    {{ message }}
  </p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clickHandler: function() {
      alert('shift + click')
    }
  }
})
```

この場合は、v-on:clickに.shiftというキー修飾子を使っている。

##v-on省略記法

```
完全
v-on:click

省略記法
@click 
```

```html
<div id="app">
<button v-on:click="clickHandler">
  Click1
</button>
<button @click="clickHandler">
  Click2
</button>
  
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16"></script>
```

```js
var app = new Vue({
  //options
  el: '#app',
  data: {
    message: ''
  },
  methods: {
    //引数名は任意につけること。
    clickHandler: function() {
      alert('Clicked!')
    }
  }
})
```

click1ボタンとclick2ボタンはどちらも同じ処理を行っている。