Knockout.js 使い方メモ

Knockout.js を勉強したときのメモ。

# Knockout.js とは
クライアントサイドの JavaScript MVVM フレームワーク。

Backbone.js と比べると、データバインディングなどの機能が備わっているなどやや高機能で、
Angular.js と比べると機能が少ない分簡潔で覚えることが少なく、かつ軽量という特徴がある。

# Hello World
## インストール
[公式サイト](http://knockoutjs.com/) より js ファイルをダウンロードする。

## 実装
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <h1 data-bind="text: message"></h1>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    var viewModel = {message: 'Hello Knockout.js!!'};
    ko.applyBindings(viewModel);
};
```

## 動作確認
![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/954676c2-b812-2c13-b6d8-a299c6001a80.jpeg)

## 説明
- Knockout.js では、 ViewModel は普通の JavaScript オブジェクトを使用する。
- ViewModel と View を紐付けるには、 `ko.applyBindings()` 関数を使用する。
- View （HTML）では、 `data-bind` 属性を使用して ViewModel の値の出力を定義する。
- この時点では、 JavaScript 側の値が変わっても、 View の表示がリアルタイムで更新されることはない（observable を使用する）。

# 入力フォームに出力する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="text" data-bind="value: message" />
  </body>
</html>
```

```js:
window.onload = function() {
    var viewModel = {message: 'Hello Knockout.js!!'};
    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/300f6135-be6b-7d1a-f7e5-2c79d8a87146.jpeg)

- `data-bind="value: xxxx"` とすることで、入力フォームに出力できる。

# 双方向のデータバインディング
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="text" data-bind="value: message" />
    <button id="button">...</button>
  </body>
</html>
```

```js:
window.onload = function() {
    var viewModel = {
        message: ko.observable('Hoge')
    };
    ko.applyBindings(viewModel);
    
    document.getElementById('button').onclick = function() {
        alert('viewModel.message=' + viewModel.message());
    };
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a8246ed0-a255-d85f-878e-19962e266da0.jpeg)

↓テキストボックスの内容を書き変えてから、ボタンをクリック

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4a9f4af2-e5cf-3e98-d3c8-8f7f6b0ff506.jpeg)

- `ko.observable()` 関数を ViewModel のプロパティに設定することで、そのプロパティの双方向のバインディングが可能になる。
- `ko.observable()` の引数には、初期値を設定する。
- `ko.observable()` の戻り値は関数になっていて、引数を渡すかどうかによって getter と setter の振る舞いをする。
    - `viewModel.message()` のように引数を渡さずに実行した場合は、プロパティの値を取得する。
    - `viewModel.message('aaa')` のように引数を渡して実行した場合は、プロパティに値を設定する。
- このように、 Knockout.js の監視対象となり双方向のデータバインディングが可能になったプロパティを `Observable` と呼ぶ。

# 複数の Observable を組み合わせた別の Observable を定義する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="text" data-bind="value: hoge" />
    <input type="text" data-bind="value: fuga" />
    <span data-bind="text: piyo"></span>
  </body>
</html>
```

```js:
window.onload = function() {
    var ViewModel = function() {
        this.hoge = ko.observable('Hoge');
        this.fuga = ko.observable('Fuga');
        
        this.piyo = ko.computed(function() {
            return this.hoge() + this.fuga();
        }, this);
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/52f08bda-44e4-3b3f-8044-6b8959953334.jpeg)

↓Hoge の値を変更する

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d882f754-75a5-fbd9-2fae-43175a8e291d.jpeg)

↓Fuga の値を変更する

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d8469784-558d-57d6-23e5-2e139fb6b1c2.jpeg)

- `ko.computed()` 関数を使うと、他の Observable を複数組み合わせた、別の Observable を定義できる。
- `ko.computed()` の第１引数には評価結果を返す関数を、第２引数には第１引数の関数内で使用する `this` が参照するオブジェクトを渡す。
- 評価関数内で他の Obervable の getter を使用すると、その Observable が変更されるたびに評価関数がコールバックされるようになる。

## computed の仕組み
`computed()` が、どうやって他の Observable の変更と連動しているのかの仕組みについて。

以下のようなロジックで連動が行われている。

1. `computed()` に渡された評価関数を実行する。
2. 実行中に呼び出された Obervable を依存対象として記録する。
3. ２で記録した Observable の値が変更された場合は、依存対象の Observable をクリアして１に戻る。

ステップ３で一旦依存する Observable をクリアしているため、評価関数が実行されるたびに依存する Observable が更新されることになる。
これによって、動的に依存する Observable が切り換わる場合にも対応できるようになっている。

しかし、言い換えると**しょっぱなの compute() の呼び出して getter が実行されなかった Obervable は、連動の対象外となる** ことを意味する。

```js:
window.onload = function() {
    
    var ViewModel = function() {
        this.flag = ko.observable(true);
        this.hoge = ko.observable('hoge');
        this.fuga = ko.observable('fuga');
        
        this.piyo = ko.computed(function() {
            return this.flag() ? this.hoge() : this.fuga();
        }, this);
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

- 初回の `computed()` の実行では、 `this.hoge()` しか実行されない。
- したがって、 `fuga` の値が変更されても `computed()` に渡した評価関数は実行されない。

# テキスト及び表現
## 表示・非表示の切り替え
```js:
window.onload = function() {
    var ViewModel = function() {
        this.isTrue = true;
        this.isFalse = false;
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="visible: isTrue">isTrue</span>
    <span data-bind="visible: isFalse">isFalse</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/ba11a5e7-1c26-700a-6270-d96df12de52a.jpeg)

- `visible` バインディングを使うと、 DOM 表示・非表示を切り替えられる。
- パラメタ（`isTrue`, `isFalse`）には、任意の式を渡すことができる。

## HTML を出力する
```js:
window.onload = function() {
    var ViewModel = function() {
        this.message = '<b>binding</b>';
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="html: message">isTrue</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f3a37cef-d42c-b0ef-e716-bdce3c687c1e.jpeg)

- `html` バインディングで、 HTML をそのまま出力することができる。
- サニタイズはされないので、信頼できる HTML だけを出力するように注意しなければならない。

## CSS クラスを設定する
### クラス名を直接指定する
```js:
window.onload = function() {
    var ViewModel = function() {
        this.cssClass = 'error italic'
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
    
    <style>
      .error {
        color: red;
      }
      
      .italic {
        font-style: italic;
      }
    </style>
  </head>
  <body>
    <span data-bind="css: cssClass">error message</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e72a8595-f4db-8428-f180-6db6263c819e.jpeg)

- `css` バインディングを使うと、 CSS クラスを設定することができる。
- パラメタには、クラス名を文字列でそのまま指定する。

### クラスごとに boolean で有効・無効を指定する
```js:
window.onload = function() {
    var ViewModel = function() {
        this.param = {
            error: true,
            italic: true
        };
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>

    <style>
      .error {
        color: red;
      }

      .italic {
        font-style: italic;
      }
    </style>
  </head>
  <body>
    <span data-bind="css: param">error message</span>
  </body>
</html>
```

- パラメタにオブジェクトを指定すると、フィールド名に対応する CSS クラスが設定される。
- フィールドの値が true （と判定される値）の場合は、その CSS クラスが有効になる。

HTML 側で以下のように指定することもできる。

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
    
    <style>
      .error {
        color: red;
      }
      
      .italic {
        font-style: italic;
      }
    </style>
  </head>
  <body>
    <span data-bind="css: {error: isError, italic: isItalic}">error message</span>
  </body>
</html>
```

## CSS スタイルを指定する
```js:
window.onload = function() {
    var ViewModel = function() {
        this.param = {
            color: 'red',
            fontStyle: 'italic'
        };
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
    
  </head>
  <body>
    <span data-bind="style: param">error message</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/076bbba9-d3c6-5587-8cd6-adc74f7c3724.jpeg)

- `style` バインディングを使うと、 CSS のスタイルを直接指定することができる。
- パラメタにはオブジェクトを渡し、プロパティ名を CSS のスタイル属性と一致させる。
- スタイル属性名にハイフンが含まれる場合は、そのままだと JavaScript のプロパティ名として使用できないので、 camelCase に置き換えて設定する。
- プロパティの値に、具体的な属性値を指定する。

## タグの属性値を設定する
```js:
window.onload = function() {
    var ViewModel = function() {
        this.param = {
            href: 'http://www.google.co.jp',
            title: 'Google'
        };
    };
    
    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
    
  </head>
  <body>
    <a data-bind="attr: param">link</a>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/76ae7579-9cc7-92bd-2cea-cac143869d27.jpeg)

- `attr` バインディングを使うと、タグの属性値を設定できる。
- パラメタにはオブジェクトを渡す。フィールド名に指定したい属性名を設定する。
- 属性名にハイフンが含まれる場合は、 `'aaa-bbb'` のようにフィールドを文字列で指定する。

# フロー制御
## 繰り返し処理
```js:
window.onload = function() {
    var viewModel = {
        array: ['hoge', 'fuga', 'piyo']
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul data-bind="foreach: array">
      <li data-bind="text: $data"></li>
    </ul>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/f999f16d-c87d-7ca4-9e1b-12273911e5c3.jpeg)

- `foreach` バインディングを使用すると、指定した配列を繰り返し処理しながら DOM を生成することができる。
- 繰り返し処理中の各要素は `$data` で参照できる。

### 要素の参照に別名を指定する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul data-bind="foreach: {data: array, as: 'element'}">
      <li data-bind="text: element"></li>
    </ul>
  </body>
</html>
```

- パラメタに `{data: <処理対象のオブジェクト>, as: '<別名>'}` と指定することで、繰り返し中の要素に別名を付けることができる。

### 現在のインデックスを参照する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul data-bind="foreach: array">
      <li>
        <span data-bind="text: $index"></span> : <span data-bind="text: $data"></span>
      </li>
    </ul>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/27438eff-0e17-0c09-73b9-170d47f212b7.jpeg)

- `$index` で現在のインデックスを取得できる。

### 繰り返しのためのタグを使用しないで foreach を使う
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul>
      <!-- ko foreach: array -->
      <li data-bind="text: $data"></li>
      <!-- /ko -->
    </ul>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/27a71299-c3ac-2869-7b24-1a9427d7482f.jpeg)

- コメントを利用した **コンテナレス構文** を使用することで、繰り返しのためのタグを使うこと無く `foreach` を使用することができる。

### 配列を Observable にする
```js:
window.onload = function() {
    var viewModel = {
        array: ko.observableArray([
            'hoge',
            'fuga',
            'piyo'
        ])
    };
    
    ko.applyBindings(viewModel);
    
    document.getElementById('add').onclick = function() {
        viewModel.array.push('add');
    };
    
    document.getElementById('remove').onclick = function() {
        viewModel.array.pop();
    };
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul data-bind="foreach: array">
      <li data-bind="text: $data"></li>
    </ul>
    
    <button id="add">add</button>
    <button id="remove">remove</button>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e08386b8-ba94-ac69-6fe9-93f6212a3baf.jpeg)

↓ `add` ボタンをクリック

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/66421c0b-9c37-bc8c-f106-5b5a5d6a98b1.jpeg)

↓ `remove` ボタンをクリック

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e08386b8-ba94-ac69-6fe9-93f6212a3baf.jpeg)

- `ko.observableArray()` 関数で、配列用の Observable オブジェクトを作成できる。
- このオブジェクトを **Observable Array** と呼ぶ。

#### Observable Array で使用できるメソッド
##### 基本
- Observable Array には、通常の JavaScript の Array クラスで使用できるものと同じメソッドが用意されている。
- IE8 以前では使用できない `indexOf()` メソッドも、 Observable Array では使用できる。

##### 生の配列を取得する
```js:
var array = ko.observableArray([
                'hoge',
                'fuga',
                'piyo'
            ]);

console.log(array()); //=> ["hoge", "fuga", "piyo"]
```

- Observable Array を関数として実行すると、生の配列が取得できる。

##### 追加されているメソッド
Observable Array には、以下のメソッドが追加されている。

###### remove(item)
```js:
var array = ko.observableArray([
                'hoge',
                'fuga',
                'hoge'
            ]);

var removed = array.remove('hoge');

console.log('removed = ' + removed); //=> removed = hoge,hoge
console.log('array = ' + array());   //=> array = fuga
```

- `remove(<削除したい要素>)` で、該当する全ての要素が削除される。
- 戻り値に、削除された要素が配列で返される。

###### remove(Function)
```js:
var array = ko.observableArray([
                'a',
                'bb',
                'ccc'
            ]);

var removed = array.remove(function(item) {
                  return 1 < item.length
              });

console.log('removed = ' + removed); //=> removed = bb,ccc
console.log('array = ' + array());   //=> arrary = a
```

- `remove(Function)` で、各要素ごとに削除するかどうかの判定を行うことができる。
- 関数が `true` を返した場合に、その要素が削除される。

###### removeAll(Array)
```js:
var array = ko.observableArray([
                'hoge',
                'fuga',
                'piyo',
                'fuga'
            ]);

var removed = array.removeAll(['hoge', 'fuga']);

console.log('removed = ' + removed); //=> removed = hoge,fuga,fuga
console.log('array = ' + array());   //=> array = piyo
```

- `removeAll(Array)` で、引数で渡した配列の各要素に一致する要素が削除される。

###### removeAll()
```js:
var array = ko.observableArray([
                'hoge',
                'fuga',
                'piyo'
            ]);

var removed = array.removeAll();

console.log('removed = ' + removed); //=> removed = hoge,fuga,piyo,fuga
console.log('array = ' + array());   //=> array =
```

- `removeAll()` で、全ての要素を削除できる。

## DOM の出力を切り替える
```js:
window.onload = function() {
    var viewModel = {
        isTrue: true,
        isFalse: false
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="if: isTrue">isTrue</span>
    <span data-bind="if: isFalse">isFalse</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7d10ca88-40a1-b5da-e981-c71a9199fa68.jpeg)

- `if` バインディングを使用すると、 DOM の出力を切り替えることができる。
- `visible` バインディングは CSS で非表示にしているだけなのに対して、 `if` バインディングは DOM を削除するという違いがある。

### コンテナレス構文を使用する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <!-- ko if: isTrue -->
      isTrue
    <!-- /ko -->
    
    <!-- ko if: isFalse-->
      isFalse
    <!-- /ko -->
  </body>
</html>
```

### 条件が偽のときだけ DOM を出力する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="ifnot: isTrue">isTrue</span>
    <span data-bind="ifnot: isFalse">isFalse</span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c3d031b3-d5b5-f621-2aa5-0b9313e718be.jpeg)

- `ifnot` バインディングを使用すると、条件式が偽の場合にのみ DOM が出力される。

## 新しいコンテキストを定義する
```js:
window.onload = function() {
    var viewModel = {
        message: 'root context message',
        subContext: {
            message: 'sub context message'
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="text: message"></span>
    
    <div data-bind="with: subContext">
      <span data-bind="text: message"></span>
    </div>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/98d0d38d-fe82-6378-f1c8-fb5ec3266e60.jpeg)

- `with` バインディングを使用すると、新しいコンテキストを定義できる。
- コンテナレス構文で使用することも可能。

# フォーム部品にバインド
## クリックイベント
```js:
window.onload = function() {
    var viewModel = {
        onClick: function() {
            console.log('click!!');
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <button data-bind="click: onClick">button</button>
  </body>
</html>
```

```text:ボタンをクリックしたときのコンソール出力
click!!
```

- `click` バインディングを使うと、クリックイベントをハンドリングできる。

### 関数に渡される引数
#### 第一引数には、その場所におけるコンテキストが渡される
```js:
window.onload = function() {
    var viewModel = {
        name: 'root',
        onClick: function(obj) {
            console.log(obj.name);
        },
        sub: {
            name: 'sub',
            onClick: function(obj) {
                console.log(obj.name);
            }
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <button data-bind="click: onClick">root</button>
    <div data-bind="with: sub">
      <button data-bind="click: onClick">sub</button>
    </div>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/dc52755b-d29d-e295-ac06-d1a949b1f6ba.jpeg)

```text:rootボタンをクリックしたときのコンソール出力
root
```

```text:subボタンをクリックしたときのコンソール出力
sub
```

#### 第二引数にはイベントオブジェクトが渡される
```js:
window.onload = function() {
    var viewModel = {
        onClick: function(obj, event) {
            console.log(event.type);
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```text:ボタン押下時のコンソール出力
click
```

## 任意のイベントにバインドする
```js:
window.onload = function() {
    var viewModel = {
        param: {
            mouseover: function() {
                console.log('over');
            },
            mouseout: function() {
                console.log('out');
            }
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
    
    <style>
      div {
        width: 100px;
        height: 100px;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div data-bind="event: param">
    </div>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a24348b5-73d9-789d-d699-e3b16bfd6b27.jpeg)

```text:マウスオーバー・アウト時のコンソール出力
over
out
```

- `event` バインディングを使用すると、任意のイベントに処理をバインドできる。
- パラメタにはオブジェクトを渡す。
- オブジェクトのプロパティ名を、イベントの名前と一致させる。

## formのサブミットイベントにバインドする
```js:
window.onload = function() {
    var viewModel = {
        onSubmit: function() {
            console.log('submit!!');
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <form data-bind="submit: onSubmit">
      <input type="text" />
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>
```

```text:サブミットじのコンソール出力
submit!!
```

- `submit` バインディングを使用すると、 form のサブミットに処理をバインドできる。
- デフォルトの submit 処理は実行されない。
    - submit を実行したい場合は、コールバック関数が `true` を返すように実装する。

## フォームの有効・無効を設定する
```js:
window.onload = function() {
    var viewModel = {
        isTrue: true,
        isFalse: false
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <button data-bind="enable: isTrue">Enable Button</button>
    <button data-bind="enable: isFalse">Dnable Button</button>
    <br>
    <button data-bind="disable: isTrue">Dnable Button</button>
    <button data-bind="disable: isFalse">Enable Button</button>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/399699b5-bead-3af0-2426-fe4016aa66be.jpeg)

- `enable` または `disable` バインディングを使用すると、フォームの有効・向こうを設定できる。


## チェックボックス（ラジオボタン）にバインディングする
### チェックボックス
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="checkbox" data-bind="checked: true" />
    <input type="checkbox" data-bind="checked: false" />
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/77aa06ca-6084-9047-6a92-eb73dfa9af2e.jpeg)

- パラメタが真の場合に、チェックボックスがチェックされる。

#### 配列を使ってチェックのオン・オフを設定する
```js:
window.onload = function() {
    var viewModel = {
        array: ['hoge', 'piyo']
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="checkbox" value="hoge" data-bind="checked: array" />
    <input type="checkbox" value="fuga" data-bind="checked: array" />
    <input type="checkbox" value="piyo" data-bind="checked: array" />
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/24f6e484-10fe-e395-9949-beb427c9f8cd.jpeg)

- パラメタに配列を指定した場合、 input タグの value で指定している値が配列に含まれる場合に、チェックがオンになる。

### ラジオボタン
```js:
window.onload = function() {
    var viewModel = {
        myRadioValue: 'fuga'
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <input type="radio" name="myRadio" value="hoge" data-bind="checked: myRadioValue" />
    <input type="radio" name="myRadio" value="fuga" data-bind="checked: myRadioValue" />
    <input type="radio" name="myRadio" value="piyo" data-bind="checked: myRadioValue" />
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/81dacadf-1de6-ab29-792a-436a8c21fee1.jpeg)

- パラメタの値と value 属性で指定した値が同じ場合に、ラジオボタンのチェックがオンになる。

## セレクトタグにバインディングする
### 基本
```js:
window.onload = function() {
    var viewModel = {
        array: ['hoge', 'fuga', 'piyo']
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <select data-bind="options: array"></select>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/81f8e23b-255f-5e2e-2895-e1c2597361eb.jpeg)

- `options` バインディングを使うと、 select タグの中身（option タグ）を生成することができる。
- パラメタに配列を渡すと、各要素がそのまま option タグの value とラベルに使用される。

### 初期選択値を指定する
```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <select data-bind="options: array, value: 'fuga'"></select>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9cc8faaf-354b-c357-e3b5-3b92ad807c13.jpeg)

- value で初期選択値を指定できる。

### ラベルと値を指定する
```js:
window.onload = function() {
    var viewModel = {
        array: [{
            id: 11,
            name: 'HOGE'
        }, {
            id: 22,
            name: 'FUGA'
        }, {
            id: 33,
            name: 'PIYO'
        }]
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <select data-bind="options: array,
                       optionsValue: 'id',
                       optionsText: 'name'"></select>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/9bd2f8ac-d7b4-9ad5-fda3-aecd24e6a0d6.jpeg)

- `options` パラメタに渡す配列の各要素をオブジェクトにする。
- `optionsValue` パラメタで、 `value` 属性に設定するプロパティ名を指定する。
- `optionsText` パラメタで、ラベルに設定するプロパティ名を指定する。

### ラベルと値に関数の処理結果を渡す
```js:
window.onload = function() {
    var viewModel = {
        array: [{
            id: 11,
            name: 'HOGE'
        }, {
            id: 22,
            name: 'FUGA'
        }, {
            id: 33,
            name: 'PIYO'
        }],
        toId: function(item) {
            return 'id_' + item.id;
        },
        toLabel: function(item) {
            return 'Label : ' + item.name;
        }
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <select data-bind="options: array,
                       optionsValue: toId,
                       optionsText: toLabel"></select>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a139580b-34e4-cb2d-d1a9-9d8cde6c0cd2.jpeg)

- `optionsValue` と `optionsLabel` に関数を渡す。
- 関数は `options` パラメタで指定した配列をイテレートするのに使用され、引数に配列の要素が順次渡される。
- 関数の戻り値が、それぞれ `value` とラベルに使用される。

### 複数選択可能な場合の初期値設定
```js:
window.onload = function() {
    var viewModel = {
        array: ['hoge', 'fuga', 'piyo'],
        selectedArray: ['hoge', 'piyo']
    };
    
    ko.applyBindings(viewModel);
};
```

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    
    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <select size="3" multiple
            data-bind="options: array,
                       selectedOptions: selectedArray"></select>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e3138dd1-4ce7-8132-023d-b2e995864a2a.jpeg)

- 複数選択可な select ボックスに初期値を設定する場合は、 `selectedOptions` パラメタを指定する。
- `selectedOptions` パラメタには、選択対象にしたい値を配列に詰めて渡す。

# テンプレート
## 基本
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="template: {name: 'myTemplate', data: myModel}">
    </div>

    <script type="text/html" id="myTemplate">
      <i data-bind="text: message"></i>
    </script>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    var viewModel = {
        myModel: {
            message: 'Hello Template!!'
        }
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/53b2e9ae-bcb2-cdf3-5aae-a725adbdb171.jpeg)

- `template` バインディングを使うと、テンプレートを使用したタグの埋め込みができる。
- テンプレートは、`<script type="text/html">` タグを使って定義する。
- `name` パラメタには、テンプレートの `<script>` タグで定義した `id` 属性を指定する。
- `data` パラメタには、テンプレート内で参照できるモデルを指定する。

## 条件によって表示・非表示を切り替える
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="template: {name: 'myTemplate', if: false}">
    </div>

    <hr>

    <div data-bind="template: {name: 'myTemplate', if: true}">
    </div>

    <script type="text/html" id="myTemplate">
      <h3>Template Block</h3>
    </script>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/505e2076-7b84-4935-321d-09dc762d7912.jpeg)

- `if` パラメタを指定すると、 true のときだけテンプレートが出力されるようになる。

## テンプレートを繰り返し出力する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <ul data-bind="template: {name: 'myTemplate', foreach: array}">
    </ul>

    <script type="text/html" id="myTemplate">
      <li data-bind="text: message"></li>
    </script>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    var viewModel = {
        array: [
            {message: 'hoge'},
            {message: 'fuga'},
            {message: 'piyo'}
        ]
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/898cd240-9b4f-145d-50bb-9eb436ed343f.jpeg)

- `foreach` パラメタに配列を指定することで、その要素数だけテンプレートが繰り返し処理される。

## テンプレートがネストされているときに、親テンプレートの値を参照する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="template: {name: 'parentTemplate', data: parentModel, as: 'parent'}">
    </div>

    <script type="text/html" id="parentTemplate">
      <div data-bind="template: {name: 'childTemplate', data: childModel}">
      </div>
    </script>

    <script type="text/html" id="childTemplate">
      parent.name = <span data-bind="text: parent.name"></span><br>
      child.name = <span data-bind="text: name"></span>
    </script>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    var viewModel = {
        parentModel: {
            name: 'ParentName',
            childModel: {
                name: 'ChildName'
            }
        }
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/4bb83560-ea46-2395-2ab2-36ee633c954b.jpeg)

- `as` パラメタを使うと、テンプレートに渡されたモデルを参照するための名前が指定できる。
- テンプレートが１階層までしかない場合は必要ないが、テンプレートが入れ子になっていて、子のテンプレートが親テンプレートの値を参照したい場合は `as` で指定が必要になる。

## テンプレートが DOM に反映された直後に処理を挟む
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="template: {name: 'myTemplate', afterRender: onAfterRender}"></div>

    <script type="text/html" id="myTemplate">
      <h2 id="msg"></h2>
    </script>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    var viewModel = {
        onAfterRender: function() {
            document.getElementById('msg').innerText = 'after render';
        }
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/91f4fb3e-ff98-de47-64fa-54d7eda7a5ea.jpeg)

- `afterRender` オプションを指定すると、テンプレートが DOM に反映された直後に処理を挟むことができる。

# １つのタグに複数のバインディングを適用する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <span data-bind="text: 'Hello Knockout.js', style: {color: 'blue'}"></span>
  </body>
</html>
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a643d210-5050-60cc-c94a-6fdf28510f29.jpeg)

- `,` カンマ区切りで複数のバインディングを適用できる。

# バインディングを自作する
## 基本
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="myBinding"></div>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            element.innerText = 'Hello Custom Binding!!';
        }
    };

    ko.applyBindings();
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/2eb99c7c-d262-c385-a999-f24716bb1cc6.jpeg)

- `ko.bindingHandlers` にプロパティを追加することで、自作のバインディングを定義することができる。
- バインディングが実行されるときに、 `init()` メソッドが実行されるので、そこで処理を実行する。
- `element` に、カスタムバインディングが適用されている対象の DOM オブジェクトが渡される。

## パラメータの値を取得する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="myBinding: 101"></div>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            var param = valueAccessor();
            element.innerText = 'param = ' + param;
        }
    };

    ko.applyBindings();
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/41a7a757-502d-3701-d193-4aaeb0174ba2.jpeg)

- `valueAccessor` を引数なしの関数として実行すると、パラメータに渡された値を取得できる。

### パラメータが Observable だった場合
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="myBinding: myModel"></div>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            var param = valueAccessor();
            element.innerText = 'param = ' + param;
        }
    };

    var viewModel = {
        myModel: ko.observable(201)
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/6418005c-7474-879b-f535-beed0d70709f.jpeg)

パラメータに渡された値が Observable だった場合、`valueAccessor()` で取得しただけでは生の値を取得できない。
しかし、取得した値が Observable かどうかをいちいち判定するのは面倒。
`valueAccessor()` の戻り値が Observable かどうかにかかわらず生の値を取得したい、という場合は `ko.unwarp()` 関数を使う。

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            var param = valueAccessor();

            var msg = 'observableValue=' + ko.unwrap(param.observableValue)
                    + ', standardValue=' + ko.unwrap(param.standardValue);

            element.innerText = msg;
        }
    };

    var viewModel = {
        myModel: {
            observableValue: ko.observable(201),
            standardValue: 301
        }
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/cce0491c-5bad-eb89-739c-d9425b9effc9.jpeg)

- `ko.unwrap()` 関数に値を渡すと、 Observable ならその生の値を、もともと生の値ならその値をそのまま返してくれる。
- なので、パラメータに指定された値が Observable なのかどうかを考慮しなくて済むようになる。

## 同じタグに指定されている別のバインディングを取得する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="myBinding, text: 'text binding'"></div>
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            element.innerText = allBindings.get('text');
        }
    };

    ko.applyBindings();
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8b78b509-1eec-97f2-abc9-c97734a50e08.jpeg)

- `allBidings.get('<取得したいバインディングの名前>')` で、他に指定されているバインディングのパラメータを取得できる。
- バインディングが指定されていない場合は、 `undefined` が返される。
- そもそもバインディングが適用されているかを知りたい場合は、 `allBindings.has('<名前>')` で確認できる。

## パラメータに指定された Observable の値が変更されたときに処理を実行する
```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />

    <script src="knockout-3.2.0.js"></script>
    <script src="sample.js"></script>
  </head>
  <body>
    <div data-bind="myBinding: message"></div>

    <input type="text" data-bind="value: message" />
  </body>
</html>
```

```js:sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            element.innerText = ko.unwrap(valueAccessor());
        },

        update: function(element, valueAccessor, allBindings) {
            element.innerText = ko.unwrap(valueAccessor());
        }
    };

    var viewModel = {
        message: ko.observable('hello custom binding')
    };

    ko.applyBindings(viewModel);
};
```

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/65885622-1a75-cb20-9abc-583294dbf860.jpeg)

↓テキストボックスの値を変更する

![knockoutjs.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fb3c0ffb-e5b6-de28-0375-9efccd5f987b.jpeg)

- パラメータが Observable の場合、値が変更されると `update()` メソッドがコールバックされる。

# 参考
- [ドキュメントKnockout.js](http://kojs.sukobuto.com/)
- [Knockout : Introduction](http://knockoutjs.com/documentation/introduction.html)
- [Knockout.js で DOM 要素をバインドするためのカスタムバインディングを作ってみた - present](http://tnakamura.hatenablog.com/entry/20120307/knockout_custom_binding)