体で覚えるVue.js - ディレクティブ編 〜 JSおくのほそ道 #023

こんにちは、ほそ道です。

今回も引き続きVue.jsのディレクティブを体で覚えます。
前回のビューモデル生成編同様、シンプルなサンプルに沢山触れる事でどんな道具があるのかをバンバン体験していくスタイルでやってまいります。

[ViewModel生成編](http://qiita.com/setzz/items/3faf74b7141081731a75)
[ディレクティブ編](http://qiita.com/setzz/items/25041c1d46452de84aa6)
[インスタンスメンバ編]
(http://qiita.com/setzz/items/ebbfcc3565bcd27f344c)
[グローバルメソッド編]
(http://qiita.com/setzz/items/8f06f2fe6049173b17f9)
[フィルター編]
(http://qiita.com/setzz/items/e37c47d3f22e5738eb84)
[v-repeatネスト編]
(http://qiita.com/setzz/items/6f22ebd15bcc0afe1ab0)
[全体の目次はこちら](http://qiita.com/setzz/items/ad9a9c99a4fda041a563)

# ディレクティブ
ビューに記述するアレですね。
デザイナーさんは最低限ディレクティブさえ覚えておけば困る事はないはずです。
Vue.jsのディレクティブはデフォルトで「v-」という接頭辞がつきます。
それでは今回は気合いをいれて全部のディレクティブについてサンプルを作ってみたいと思いますので順繰りに見ていきましょう。

## v-text

DOM要素の内側をテキストノードとしてすべて書き換える
`{{property}}`の形式もテキストノードとして変換され部分的な書き換えを行う場合はこちらを使うと良さそう

```html:FOOとHelloBARを表示
<div id="sample">
  <div v-text="foo"></div>
  <div>Hello{{bar}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'FOO',
      bar: 'BAR'
    }
  });
</script>
```

## v-html

DOM要素の内側をinnerHTMLとして書き換える
v-textはHTML文字列をただの文字列として解釈するが、v-htmlはHTMLとして扱う

```html:v-text側は&lt;p&gt;FOO&lt;/p&gt;を表示し、v-html側はFOOを表示する
<div id="sample">
  <div v-text="foo"></div>
  <div v-html="foo"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: '<p>FOO</p>'
    }
  });
</script>
```

## v-show

参照する値がtrueとして評価され場合は表示し、falseとして評価される場合はdisplay:none等のスタイルが付いて非表示になる
要素の内側に値が表示される事は無い

```html:fooを参照しているdivは表示され、barを参照しているdivは非表示になる
<div id="sample">
  <div v-show="foo" style="height: 100px; width:100px; background:red"></div>
  <div v-show="bar" style="height: 100px; width:100px; background:blue"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 1,
      bar: 0
    }
  });
  vm.bar = 1;  // ここで要素が表示される
</script>
```

## v-class

2つの用法があり、引数無しの場合はバインドされた値がクラス名となる
引数ありの場合はバインドされた値がtrueと評価される場合だけクラス名が設定される

```html:上のdivが引数なし、下のdivが引数あり(trueのデータを参照するクラスのみ設定される)
<div id="sample">
  <div v-class="foo, bar"></div>
  <div v-class="class1: t, class2: f"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'FOO',
      bar: 'BAR',
      t: true,
      f: false
    }
  });
</script>
```

## v-attr

DOM要素の属性を設定する
属性の内部で`{{property}}`を設定した場合はv-attrとして扱われる

```html:２つのやり方で属性を設定
<div id="sample">
  <div v-attr="style:foo">hosomichi</div>
  <div class="{{bar}}"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'font-size:30px',
      bar: 'BAR'
    }
  });
</script>
```

## v-style

DOM要素のスタイルを設定する

```html:スタイルをセット
<div id="sample" v-style="foo">hosomichi</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'font-size:30px',
    }
  });
</script>
```

## v-on

DOM要素にイベントリスナーを登録する

```html:クリックイベントを設定
<div id="sample" v-on="click: foo">ClickHere!</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    methods:{
      foo: function(){alert('clicked!');}
    }
  });
</script>
```

## v-model

v-textと同様にDOM要素の内側をテキストノードとしてすべて書き換える
また編集可能な要素にセットする事でプロパティをリアルタイムで変更する事が出来るようになる

```html:テキストボックスの編集に同期してdivのインナーテキストが変更される
<form id="sample" action="">
  <div v-text="foo"></div>
  <input type="text" v-model="foo">
</form>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'defaultValue',
    }
  });
</script>
```

## v-if

バインドした値がtrue評価であればDOM要素が生成され、falseであれば破棄される
v-showと似ているがこちらは要素自体を生成/破棄する

```html:vm.fooの値を書き換える事で表示/非表示が変化
<div id="sample">
  <div v-if="foo">Element</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data:{
      foo: false
    }
  });
  vm.foo = 1;  // ここで要素が表示される
</script>
```

## v-repeat

DOM要素を繰り返し生成する事が出来る
バインドするデータはオブジェクトリテラルか配列のみ可能
配列の場合は`$value`でアクセス出来る
オブジェクトリテラルの場合はキーが`$key`、値が`$value`でアクセス出来る

```html:配列をバインドするパターン
<div id="sample">
  <div v-repeat="foo">{{$value}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data:{
      foo: [100, 200,  300,  400,  500]
    }
  });
</script>
```

```html:オブジェクトリテラルをバインドするパターン
<div id="sample">
  <div v-repeat="foo">{{$key}} = {{$value}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data:{
      foo: {
        a: 1,
        b: 2,
        c: 3
      }
    }
  });
</script>
```

## v-view

DOM要素の内側をコンポーネントで置換する

```html:ビューモデルのコンポーネントを使う(値の指定時にシングルクォートも必要な模様？)
<div id="sample">
  <div v-view="'foo'"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    components: {
      foo: {
        template: '<p>Custom View</p>'
      }
    }
  });
</script>
```

```html:ex.グローバルコンポーネントを登録して使う
<div id="sample">
  <div v-view="foo"></div>
</div>
<script src="js/vue.js"></script>
<script>
  Vue.component('view1', {template: '<p>View Component</p>'} );
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'view1'
    }
  });
</script>
```

## v-ref

componentのデータをビューモデルの$変数に登録できる

```html:xとyをvm.$.fooに格納
<div id="sample">
  <div v-ref="foo" v-component="bar">value</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    components: {
      bar: {
        data: {
          x: 100,
          y: 200
        },
        template: '{{x}} and {{y}}'
      }
    }
  });
  console.log(vm.$.foo.x);
  console.log(vm.$.foo.y);
</script>
```

## v-partial

DOM要素の内側をコンポーネントで置換する
クエリーセレクタで既存のDOM要素を引っ張ってくる事も出来る

```html:クエリーセレクタとテンプレート的な使い方の両方を実行
<div id="sample">
  <div v-partial="foo"></div>
  <div v-partial="bar"></div>
</div>
<div id="other">other</div>
<script src="js/vue.js"></script>
<script>
  Vue.partial('foo', '#other');
  Vue.partial('bar', '<p>Partial</p>');
  var vm = new Vue({
    el: '#sample',
  });
</script>
```

## v-effect

v-ifやv-showで要素の表示/非表示切替時にjavascriptを実行出来る
実行する関数はenterとleaveという名前で設定する

```html:vm.barを変更する事でenter/leave関数が実行される
<div id="sample">
  <div id="inner" v-effect="foo" v-if="bar">inner</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      bar: false
    },
    effects: {
      foo: {
        enter: function(el, insert, timeout){
          insert();
          console.log('inserted');
        },
        leave: function(el, remove, timeout){
          remove();
          console.log('removed');
        },
      }
    }
  });
</script>
```

## v-transition

CSSトランジションを使う要素である事を宣言する
v-ifやv-showと組み合わせて状態変化を描画する

```html:ボタンクリックで表示/非表示をトランジションを適用させながら切替え
<style>
  .msg {
    transition: all .3s ease;
    height: 30px;
    padding: 10px;
    background-color: #eee;
    overflow: hidden;
  }
  .msg.v-enter,  .msg.v-leave {
    height: 0;
    padding: 0 10px;
    opacity: 0;
  }
</style>
<button onclick="vm.foo = vm.foo ? false : true">toggle</button>
<div id="sample">
  <p class="msg" v-if="foo" v-transition>Hello!</p>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: true
    }
  });
</script>
```

## v-animation

CSSアニメーションを使う要素である事を宣言する
v-ifやv-showと組み合わせてアニメーションを描画する

```html:ボタンクリックで表示/非表示をアニメーションを適用させながら切替え
<style>
  .msg { font-size: 30px; display: inline-block }
  .msg.v-enter { animation: fadein .5s; -webkit-animation: fadein .5s }
  .msg.v-leave { animation: fadeout .5s; -webkit-animation: fadeout .5s }
  @keyframes fadein {
    0% { transform: scale(0); }
    50% { transform: scale(1.5); }
    100% { transform: scale(1); }
  }
  @keyframes fadeout {
    0% { transform: scale(1); }
    50% { transform: scale(1.5); }
    100% { transform: scale(0); }
  }
  @-webkit-keyframes fadein {
    0% { -webkit-transform: scale(0); }
    50% { -webkit-transform: scale(1.5); }
    100% { -webkit-transform: scale(1); }
  }
  @-webkit-keyframes fadeout {
    0% { -webkit-transform: scale(1); }
    50% { -webkit-transform: scale(1.5); }
    100% { -webkit-transform: scale(0); }
  }
</style>
<button onclick="vm.foo= vm.foo? false : true">toggle</button>
<div id="sample">
  <span class="msg" v-if="foo" v-animation>Hello!</span>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: true
    }
  });
</script>
```

## v-pre

v-preをつけた要素とその子要素はデータバインディングがスキップされる
スキップされた分描画は高速になる

```html:下のdivは{{foo}}がそのまま画面に表示されてしまう
<div id="sample">
  <div>{{foo}}</div>
  <div v-pre>{{foo}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'hosomichi'
    }
  });
</script>
```

## v-cloak

CSSの`[v-cloak] { display: none}`と組み合わせて使う事で
データバインディング完了までこのディレクティブがつけられた要素の描画は遅れさせられる
初期表示時の画面のちらつきを抑える効果がある

```html:見た目にはわかりづらいがdisplay:noneを設定したdiv要素が表示される(v-cloakを外せばちゃんと非表示になる)
<style>[v-cloak] { display:none }</style>
<div id="sample">
  <div v-cloak>{{foo}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'hosomichi'
    }
  });
</script>
```

===

長かった…!!
今回は以上です。
次回は引き続きVue.jsで、Vueインスタンスのメンバを体で覚えます。