Help us understand the problem. What is going on with this article?

Knockout.js 使い方メモ

More than 5 years have passed since last update.

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

Knockout.js とは

クライアントサイドの JavaScript MVVM フレームワーク。

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

Hello World

インストール

公式サイト より js ファイルをダウンロードする。

実装

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>
sample.js
window.onload = function() {
    var viewModel = {message: 'Hello Knockout.js!!'};
    ko.applyBindings(viewModel);
};

動作確認

knockoutjs.JPG

説明

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

入力フォームに出力する

<!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>
window.onload = function() {
    var viewModel = {message: 'Hello Knockout.js!!'};
    ko.applyBindings(viewModel);
};

knockoutjs.JPG

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

双方向のデータバインディング

<!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>
window.onload = function() {
    var viewModel = {
        message: ko.observable('Hoge')
    };
    ko.applyBindings(viewModel);

    document.getElementById('button').onclick = function() {
        alert('viewModel.message=' + viewModel.message());
    };
};

knockoutjs.JPG

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

knockoutjs.JPG

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

複数の Observable を組み合わせた別の Observable を定義する

<!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>
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

↓Hoge の値を変更する

knockoutjs.JPG

↓Fuga の値を変更する

knockoutjs.JPG

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

computed の仕組み

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

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

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

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

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

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() に渡した評価関数は実行されない。

テキスト及び表現

表示・非表示の切り替え

window.onload = function() {
    var ViewModel = function() {
        this.isTrue = true;
        this.isFalse = false;
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
<!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

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

HTML を出力する

window.onload = function() {
    var ViewModel = function() {
        this.message = '<b>binding</b>';
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
<!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

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

CSS クラスを設定する

クラス名を直接指定する

window.onload = function() {
    var ViewModel = function() {
        this.cssClass = 'error italic'
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
<!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

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

クラスごとに boolean で有効・無効を指定する

window.onload = function() {
    var ViewModel = function() {
        this.param = {
            error: true,
            italic: true
        };
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
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 側で以下のように指定することもできる。

<!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 スタイルを指定する

window.onload = function() {
    var ViewModel = function() {
        this.param = {
            color: 'red',
            fontStyle: 'italic'
        };
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
<!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

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

タグの属性値を設定する

window.onload = function() {
    var ViewModel = function() {
        this.param = {
            href: 'http://www.google.co.jp',
            title: 'Google'
        };
    };

    var viewModel = new ViewModel();
    ko.applyBindings(viewModel);
};
<!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

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

フロー制御

繰り返し処理

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

    ko.applyBindings(viewModel);
};
<!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

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

要素の参照に別名を指定する

<!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: '<別名>'} と指定することで、繰り返し中の要素に別名を付けることができる。

現在のインデックスを参照する

<!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

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

繰り返しのためのタグを使用しないで foreach を使う

<!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

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

配列を Observable にする

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();
    };
};
<!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

add ボタンをクリック

knockoutjs.JPG

remove ボタンをクリック

knockoutjs.JPG

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

Observable Array で使用できるメソッド

基本
  • Observable Array には、通常の JavaScript の Array クラスで使用できるものと同じメソッドが用意されている。
  • IE8 以前では使用できない indexOf() メソッドも、 Observable Array では使用できる。
生の配列を取得する
var array = ko.observableArray([
                'hoge',
                'fuga',
                'piyo'
            ]);

console.log(array()); //=> ["hoge", "fuga", "piyo"]
  • Observable Array を関数として実行すると、生の配列が取得できる。
追加されているメソッド

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

remove(item)
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)
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)
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()
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 の出力を切り替える

window.onload = function() {
    var viewModel = {
        isTrue: true,
        isFalse: false
    };

    ko.applyBindings(viewModel);
};
<!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

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

コンテナレス構文を使用する

<!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 を出力する

<!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

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

新しいコンテキストを定義する

window.onload = function() {
    var viewModel = {
        message: 'root context message',
        subContext: {
            message: 'sub context message'
        }
    };

    ko.applyBindings(viewModel);
};
<!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

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

フォーム部品にバインド

クリックイベント

window.onload = function() {
    var viewModel = {
        onClick: function() {
            console.log('click!!');
        }
    };

    ko.applyBindings(viewModel);
};
<!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>
ボタンをクリックしたときのコンソール出力
click!!
  • click バインディングを使うと、クリックイベントをハンドリングできる。

関数に渡される引数

第一引数には、その場所におけるコンテキストが渡される

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);
};
<!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

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

第二引数にはイベントオブジェクトが渡される

window.onload = function() {
    var viewModel = {
        onClick: function(obj, event) {
            console.log(event.type);
        }
    };

    ko.applyBindings(viewModel);
};
ボタン押下時のコンソール出力
click

任意のイベントにバインドする

window.onload = function() {
    var viewModel = {
        param: {
            mouseover: function() {
                console.log('over');
            },
            mouseout: function() {
                console.log('out');
            }
        }
    };

    ko.applyBindings(viewModel);
};
<!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

マウスオーバー・アウト時のコンソール出力
over
out
  • event バインディングを使用すると、任意のイベントに処理をバインドできる。
  • パラメタにはオブジェクトを渡す。
  • オブジェクトのプロパティ名を、イベントの名前と一致させる。

formのサブミットイベントにバインドする

window.onload = function() {
    var viewModel = {
        onSubmit: function() {
            console.log('submit!!');
        }
    };

    ko.applyBindings(viewModel);
};
<!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>
サブミットじのコンソール出力
submit!!
  • submit バインディングを使用すると、 form のサブミットに処理をバインドできる。
  • デフォルトの submit 処理は実行されない。
    • submit を実行したい場合は、コールバック関数が true を返すように実装する。

フォームの有効・無効を設定する

window.onload = function() {
    var viewModel = {
        isTrue: true,
        isFalse: false
    };

    ko.applyBindings(viewModel);
};
<!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

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

チェックボックス(ラジオボタン)にバインディングする

チェックボックス

<!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

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

配列を使ってチェックのオン・オフを設定する

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

    ko.applyBindings(viewModel);
};
<!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

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

ラジオボタン

window.onload = function() {
    var viewModel = {
        myRadioValue: 'fuga'
    };

    ko.applyBindings(viewModel);
};
<!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

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

セレクトタグにバインディングする

基本

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

    ko.applyBindings(viewModel);
};
<!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

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

初期選択値を指定する

<!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

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

ラベルと値を指定する

window.onload = function() {
    var viewModel = {
        array: [{
            id: 11,
            name: 'HOGE'
        }, {
            id: 22,
            name: 'FUGA'
        }, {
            id: 33,
            name: 'PIYO'
        }]
    };

    ko.applyBindings(viewModel);
};
<!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

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

ラベルと値に関数の処理結果を渡す

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);
};
<!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

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

複数選択可能な場合の初期値設定

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

    ko.applyBindings(viewModel);
};
<!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

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

テンプレート

基本

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>
sample.js
window.onload = function() {
    var viewModel = {
        myModel: {
            message: 'Hello Template!!'
        }
    };

    ko.applyBindings(viewModel);
};

knockoutjs.JPG

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

条件によって表示・非表示を切り替える

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

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

テンプレートを繰り返し出力する

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>
sample.js
window.onload = function() {
    var viewModel = {
        array: [
            {message: 'hoge'},
            {message: 'fuga'},
            {message: 'piyo'}
        ]
    };

    ko.applyBindings(viewModel);
};

knockoutjs.JPG

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

テンプレートがネストされているときに、親テンプレートの値を参照する

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>
sample.js
window.onload = function() {
    var viewModel = {
        parentModel: {
            name: 'ParentName',
            childModel: {
                name: 'ChildName'
            }
        }
    };

    ko.applyBindings(viewModel);
};

knockoutjs.JPG

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

テンプレートが DOM に反映された直後に処理を挟む

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>
sample.js
window.onload = function() {
    var viewModel = {
        onAfterRender: function() {
            document.getElementById('msg').innerText = 'after render';
        }
    };

    ko.applyBindings(viewModel);
};

knockoutjs.JPG

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

1つのタグに複数のバインディングを適用する

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

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

バインディングを自作する

基本

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>
sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            element.innerText = 'Hello Custom Binding!!';
        }
    };

    ko.applyBindings();
};

knockoutjs.JPG

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

パラメータの値を取得する

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>
sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            var param = valueAccessor();
            element.innerText = 'param = ' + param;
        }
    };

    ko.applyBindings();
};

knockoutjs.JPG

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

パラメータが Observable だった場合

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>
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

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

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

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

同じタグに指定されている別のバインディングを取得する

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>
sample.js
window.onload = function() {
    ko.bindingHandlers.myBinding = {
        init: function(element, valueAccessor, allBindings) {
            element.innerText = allBindings.get('text');
        }
    };

    ko.applyBindings();
};

knockoutjs.JPG

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

パラメータに指定された Observable の値が変更されたときに処理を実行する

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>
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

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

knockoutjs.JPG

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

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away